Coverage Report

Created: 2025-08-11 09:23

/src/gdal/gcore/gdalproxypool.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Core
4
 * Purpose:  A dataset and raster band classes that differ the opening of the
5
 *           underlying dataset in a limited pool of opened datasets.
6
 * Author:   Even Rouault <even dot rouault at spatialys.com>
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "gdal_proxy.h"
16
17
#include <algorithm>
18
#include <cstdio>
19
#include <cstdlib>
20
#include <cstring>
21
22
#include "cpl_conv.h"
23
#include "cpl_error.h"
24
#include "cpl_hash_set.h"
25
#include "cpl_multiproc.h"
26
#include "cpl_string.h"
27
#include "gdal.h"
28
#include "gdal_priv.h"
29
30
//! @cond Doxygen_Suppress
31
32
/* We *must* share the same mutex as the gdaldataset.cpp file, as we are */
33
/* doing GDALOpen() calls that can indirectly call GDALOpenShared() on */
34
/* an auxiliary dataset ... */
35
/* Then we could get dead-locks in multi-threaded use case */
36
37
/* ******************************************************************** */
38
/*                         GDALDatasetPool                              */
39
/* ******************************************************************** */
40
41
/* This class is a singleton that maintains a pool of opened datasets */
42
/* The cache uses a LRU strategy */
43
44
class GDALDatasetPool;
45
static GDALDatasetPool *singleton = nullptr;
46
47
void GDALNullifyProxyPoolSingleton()
48
0
{
49
0
    singleton = nullptr;
50
0
}
51
52
struct _GDALProxyPoolCacheEntry
53
{
54
    GIntBig responsiblePID;
55
    char *pszFileNameAndOpenOptions;
56
    char *pszOwner;
57
    GDALDataset *poDS;
58
    GIntBig nRAMUsage;
59
60
    /* Ref count of the cached dataset */
61
    int refCount;
62
63
    GDALProxyPoolCacheEntry *prev;
64
    GDALProxyPoolCacheEntry *next;
65
};
66
67
// This variable prevents a dataset that is going to be opened in
68
// GDALDatasetPool::_RefDataset from increasing refCount if, during its
69
// opening, it creates a GDALProxyPoolDataset.
70
// We increment it before opening or closing a cached dataset and decrement
71
// it afterwards
72
// The typical use case is a VRT made of simple sources that are VRT
73
// We don't want the "inner" VRT to take a reference on the pool, otherwise
74
// there is a high chance that this reference will not be dropped and the pool
75
// remain ghost.
76
static thread_local int refCountOfDisabledRefCount = 0;
77
78
class GDALDatasetPool
79
{
80
  private:
81
    bool bInDestruction = false;
82
83
    /* Ref count of the pool singleton */
84
    /* Taken by "toplevel" GDALProxyPoolDataset in its constructor and released
85
     */
86
    /* in its destructor. See also refCountOfDisabledRefCount for the difference
87
     */
88
    /* between toplevel and inner GDALProxyPoolDataset */
89
    int refCount = 0;
90
91
    int maxSize = 0;
92
    int currentSize = 0;
93
    int64_t nMaxRAMUsage = 0;
94
    int64_t nRAMUsage = 0;
95
    GDALProxyPoolCacheEntry *firstEntry = nullptr;
96
    GDALProxyPoolCacheEntry *lastEntry = nullptr;
97
98
    /* Caution : to be sure that we don't run out of entries, size must be at */
99
    /* least greater or equal than the maximum number of threads */
100
    explicit GDALDatasetPool(int maxSize, int64_t nMaxRAMUsage);
101
    ~GDALDatasetPool();
102
    GDALProxyPoolCacheEntry *_RefDataset(const char *pszFileName,
103
                                         GDALAccess eAccess,
104
                                         CSLConstList papszOpenOptions,
105
                                         int bShared, bool bForceOpen,
106
                                         const char *pszOwner);
107
    void _CloseDatasetIfZeroRefCount(const char *pszFileName,
108
                                     CSLConstList papszOpenOptions,
109
                                     GDALAccess eAccess, const char *pszOwner);
110
111
#ifdef DEBUG_PROXY_POOL
112
    // cppcheck-suppress unusedPrivateFunction
113
    void ShowContent();
114
    void CheckLinks();
115
#endif
116
117
    CPL_DISALLOW_COPY_ASSIGN(GDALDatasetPool)
118
119
  public:
120
    static void Ref();
121
    static void Unref();
122
    static GDALProxyPoolCacheEntry *RefDataset(const char *pszFileName,
123
                                               GDALAccess eAccess,
124
                                               char **papszOpenOptions,
125
                                               int bShared, bool bForceOpen,
126
                                               const char *pszOwner);
127
    static void UnrefDataset(GDALProxyPoolCacheEntry *cacheEntry);
128
    static void CloseDatasetIfZeroRefCount(const char *pszFileName,
129
                                           CSLConstList papszOpenOptions,
130
                                           GDALAccess eAccess,
131
                                           const char *pszOwner);
132
133
    static void PreventDestroy();
134
    static void ForceDestroy();
135
};
136
137
/************************************************************************/
138
/*                         GDALDatasetPool()                            */
139
/************************************************************************/
140
141
GDALDatasetPool::GDALDatasetPool(int maxSizeIn, int64_t nMaxRAMUsageIn)
142
880
    : maxSize(maxSizeIn), nMaxRAMUsage(nMaxRAMUsageIn)
143
880
{
144
880
}
145
146
/************************************************************************/
147
/*                        ~GDALDatasetPool()                            */
148
/************************************************************************/
149
150
GDALDatasetPool::~GDALDatasetPool()
151
877
{
152
877
    bInDestruction = true;
153
877
    GDALProxyPoolCacheEntry *cur = firstEntry;
154
877
    GIntBig responsiblePID = GDALGetResponsiblePIDForCurrentThread();
155
2.83k
    while (cur)
156
1.96k
    {
157
1.96k
        GDALProxyPoolCacheEntry *next = cur->next;
158
1.96k
        CPLFree(cur->pszFileNameAndOpenOptions);
159
1.96k
        CPLFree(cur->pszOwner);
160
1.96k
        CPLAssert(cur->refCount == 0);
161
1.96k
        if (cur->poDS)
162
0
        {
163
0
            GDALSetResponsiblePIDForCurrentThread(cur->responsiblePID);
164
0
            GDALClose(cur->poDS);
165
0
        }
166
1.96k
        CPLFree(cur);
167
1.96k
        cur = next;
168
1.96k
    }
169
877
    GDALSetResponsiblePIDForCurrentThread(responsiblePID);
170
877
}
171
172
#ifdef DEBUG_PROXY_POOL
173
/************************************************************************/
174
/*                            ShowContent()                             */
175
/************************************************************************/
176
177
void GDALDatasetPool::ShowContent()
178
{
179
    GDALProxyPoolCacheEntry *cur = firstEntry;
180
    int i = 0;
181
    while (cur)
182
    {
183
        printf("[%d] pszFileName=%s, owner=%s, refCount=%d, " /*ok*/
184
               "responsiblePID=%d\n",
185
               i,
186
               cur->pszFileNameAndOpenOptions ? cur->pszFileNameAndOpenOptions
187
                                              : "(null)",
188
               cur->pszOwner ? cur->pszOwner : "(null)", cur->refCount,
189
               (int)cur->responsiblePID);
190
        i++;
191
        cur = cur->next;
192
    }
193
}
194
195
/************************************************************************/
196
/*                             CheckLinks()                             */
197
/************************************************************************/
198
199
void GDALDatasetPool::CheckLinks()
200
{
201
    GDALProxyPoolCacheEntry *cur = firstEntry;
202
    int i = 0;
203
    while (cur)
204
    {
205
        CPLAssert(cur == firstEntry || cur->prev->next == cur);
206
        CPLAssert(cur == lastEntry || cur->next->prev == cur);
207
        ++i;
208
        CPLAssert(cur->next != nullptr || cur == lastEntry);
209
        cur = cur->next;
210
    }
211
    (void)i;
212
    CPLAssert(i == currentSize);
213
}
214
#endif
215
216
/************************************************************************/
217
/*                       GetFilenameAndOpenOptions()                    */
218
/************************************************************************/
219
220
static std::string GetFilenameAndOpenOptions(const char *pszFileName,
221
                                             CSLConstList papszOpenOptions)
222
103k
{
223
103k
    std::string osFilenameAndOO(pszFileName);
224
105k
    for (int i = 0; papszOpenOptions && papszOpenOptions[i]; ++i)
225
1.61k
    {
226
1.61k
        osFilenameAndOO += "||";
227
1.61k
        osFilenameAndOO += papszOpenOptions[i];
228
1.61k
    }
229
103k
    return osFilenameAndOO;
230
103k
}
231
232
/************************************************************************/
233
/*                            _RefDataset()                             */
234
/************************************************************************/
235
236
GDALProxyPoolCacheEntry *
237
GDALDatasetPool::_RefDataset(const char *pszFileName, GDALAccess eAccess,
238
                             CSLConstList papszOpenOptions, int bShared,
239
                             bool bForceOpen, const char *pszOwner)
240
81.9k
{
241
81.9k
    CPLMutex **pMutex = GDALGetphDLMutex();
242
81.9k
    CPLMutexHolderD(pMutex);
243
244
81.9k
    if (bInDestruction)
245
0
        return nullptr;
246
247
81.9k
    const GIntBig responsiblePID = GDALGetResponsiblePIDForCurrentThread();
248
249
81.9k
    const auto EvictEntryWithZeroRefCount =
250
81.9k
        [this, responsiblePID](bool evictEntryWithOpenedDataset)
251
81.9k
    {
252
11.5k
        GDALProxyPoolCacheEntry *cur = firstEntry;
253
11.5k
        GDALProxyPoolCacheEntry *candidate = nullptr;
254
1.16M
        while (cur)
255
1.15M
        {
256
1.15M
            GDALProxyPoolCacheEntry *next = cur->next;
257
258
1.15M
            if (cur->refCount == 0 &&
259
1.15M
                (!evictEntryWithOpenedDataset || cur->nRAMUsage > 0))
260
1.15M
            {
261
1.15M
                candidate = cur;
262
1.15M
            }
263
264
1.15M
            cur = next;
265
1.15M
        }
266
11.5k
        if (candidate == nullptr)
267
0
            return false;
268
269
11.5k
        nRAMUsage -= candidate->nRAMUsage;
270
11.5k
        candidate->nRAMUsage = 0;
271
272
11.5k
        CPLFree(candidate->pszFileNameAndOpenOptions);
273
11.5k
        candidate->pszFileNameAndOpenOptions = nullptr;
274
275
11.5k
        if (candidate->poDS)
276
0
        {
277
            /* Close by pretending we are the thread that GDALOpen'ed this */
278
            /* dataset */
279
0
            GDALSetResponsiblePIDForCurrentThread(candidate->responsiblePID);
280
281
0
            refCountOfDisabledRefCount++;
282
0
            GDALClose(candidate->poDS);
283
0
            refCountOfDisabledRefCount--;
284
285
0
            candidate->poDS = nullptr;
286
0
            GDALSetResponsiblePIDForCurrentThread(responsiblePID);
287
0
        }
288
11.5k
        CPLFree(candidate->pszOwner);
289
11.5k
        candidate->pszOwner = nullptr;
290
291
11.5k
        if (!evictEntryWithOpenedDataset && candidate != firstEntry)
292
11.5k
        {
293
            /* Recycle this entry for the to-be-opened dataset and */
294
            /* moves it to the top of the list */
295
11.5k
            if (candidate->prev)
296
11.5k
                candidate->prev->next = candidate->next;
297
298
11.5k
            if (candidate->next)
299
0
                candidate->next->prev = candidate->prev;
300
11.5k
            else
301
11.5k
            {
302
11.5k
                CPLAssert(candidate == lastEntry);
303
11.5k
                lastEntry->prev->next = nullptr;
304
11.5k
                lastEntry = lastEntry->prev;
305
11.5k
            }
306
11.5k
            candidate->prev = nullptr;
307
11.5k
            candidate->next = firstEntry;
308
11.5k
            firstEntry->prev = candidate;
309
11.5k
            firstEntry = candidate;
310
311
#ifdef DEBUG_PROXY_POOL
312
            CheckLinks();
313
#endif
314
11.5k
        }
315
316
11.5k
        return true;
317
11.5k
    };
318
319
81.9k
    GDALProxyPoolCacheEntry *cur = firstEntry;
320
321
81.9k
    const std::string osFilenameAndOO =
322
81.9k
        GetFilenameAndOpenOptions(pszFileName, papszOpenOptions);
323
324
1.38M
    while (cur)
325
1.36M
    {
326
1.36M
        GDALProxyPoolCacheEntry *next = cur->next;
327
328
1.36M
        if (cur->refCount >= 0 && cur->pszFileNameAndOpenOptions &&
329
1.36M
            osFilenameAndOO == cur->pszFileNameAndOpenOptions &&
330
1.36M
            ((bShared && cur->responsiblePID == responsiblePID &&
331
69.2k
              ((cur->pszOwner == nullptr && pszOwner == nullptr) ||
332
69.2k
               (cur->pszOwner != nullptr && pszOwner != nullptr &&
333
69.2k
                strcmp(cur->pszOwner, pszOwner) == 0))) ||
334
69.2k
             (!bShared && cur->refCount == 0)))
335
68.0k
        {
336
68.0k
            if (cur != firstEntry)
337
7.16k
            {
338
                /* Move to begin */
339
7.16k
                if (cur->next)
340
5.79k
                    cur->next->prev = cur->prev;
341
1.36k
                else
342
1.36k
                    lastEntry = cur->prev;
343
7.16k
                cur->prev->next = cur->next;
344
7.16k
                cur->prev = nullptr;
345
7.16k
                firstEntry->prev = cur;
346
7.16k
                cur->next = firstEntry;
347
7.16k
                firstEntry = cur;
348
349
#ifdef DEBUG_PROXY_POOL
350
                CheckLinks();
351
#endif
352
7.16k
            }
353
354
68.0k
            cur->refCount++;
355
68.0k
            return cur;
356
68.0k
        }
357
358
1.30M
        cur = next;
359
1.30M
    }
360
361
13.8k
    if (!bForceOpen)
362
21
        return nullptr;
363
364
13.8k
    if (currentSize == maxSize)
365
11.5k
    {
366
11.5k
        if (!EvictEntryWithZeroRefCount(false))
367
0
        {
368
0
            CPLError(
369
0
                CE_Failure, CPLE_AppDefined,
370
0
                "Too many threads are running for the current value of the "
371
0
                "dataset pool size (%d).\n"
372
0
                "or too many proxy datasets are opened in a cascaded way.\n"
373
0
                "Try increasing GDAL_MAX_DATASET_POOL_SIZE.",
374
0
                maxSize);
375
0
            return nullptr;
376
0
        }
377
378
11.5k
        CPLAssert(firstEntry);
379
11.5k
        cur = firstEntry;
380
11.5k
    }
381
2.26k
    else
382
2.26k
    {
383
        /* Prepend */
384
2.26k
        cur = static_cast<GDALProxyPoolCacheEntry *>(
385
2.26k
            CPLCalloc(1, sizeof(GDALProxyPoolCacheEntry)));
386
2.26k
        if (lastEntry == nullptr)
387
880
            lastEntry = cur;
388
2.26k
        cur->prev = nullptr;
389
2.26k
        cur->next = firstEntry;
390
2.26k
        if (firstEntry)
391
1.38k
            firstEntry->prev = cur;
392
2.26k
        firstEntry = cur;
393
2.26k
        currentSize++;
394
#ifdef DEBUG_PROXY_POOL
395
        CheckLinks();
396
#endif
397
2.26k
    }
398
399
13.8k
    cur->pszFileNameAndOpenOptions = CPLStrdup(osFilenameAndOO.c_str());
400
13.8k
    cur->pszOwner = (pszOwner) ? CPLStrdup(pszOwner) : nullptr;
401
13.8k
    cur->responsiblePID = responsiblePID;
402
13.8k
    cur->refCount = -1;  // to mark loading of dataset in progress
403
13.8k
    cur->nRAMUsage = 0;
404
405
13.8k
    refCountOfDisabledRefCount++;
406
13.8k
    const int nFlag =
407
13.8k
        ((eAccess == GA_Update) ? GDAL_OF_UPDATE : GDAL_OF_READONLY) |
408
13.8k
        GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR;
409
13.8k
    CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
410
411
    // Release mutex while opening dataset to avoid lock contention.
412
13.8k
    CPLReleaseMutex(*pMutex);
413
13.8k
    auto poDS = GDALDataset::Open(pszFileName, nFlag, nullptr, papszOpenOptions,
414
13.8k
                                  nullptr);
415
13.8k
    CPLAcquireMutex(*pMutex, 1000.0);
416
417
13.8k
    cur->poDS = poDS;
418
13.8k
    cur->refCount = 1;
419
420
13.8k
    refCountOfDisabledRefCount--;
421
422
13.8k
    if (cur->poDS)
423
11.1k
    {
424
11.1k
        cur->nRAMUsage =
425
11.1k
            std::max<GIntBig>(0, cur->poDS->GetEstimatedRAMUsage());
426
11.1k
        nRAMUsage += cur->nRAMUsage;
427
11.1k
    }
428
429
13.8k
    if (nMaxRAMUsage > 0 && cur->nRAMUsage > 0)
430
0
    {
431
0
        while (nRAMUsage > nMaxRAMUsage && nRAMUsage != cur->nRAMUsage &&
432
0
               EvictEntryWithZeroRefCount(true))
433
0
        {
434
            // ok
435
0
        }
436
0
    }
437
438
13.8k
    return cur;
439
13.8k
}
440
441
/************************************************************************/
442
/*                   _CloseDatasetIfZeroRefCount()                      */
443
/************************************************************************/
444
445
void GDALDatasetPool::_CloseDatasetIfZeroRefCount(const char *pszFileName,
446
                                                  CSLConstList papszOpenOptions,
447
                                                  GDALAccess /* eAccess */,
448
                                                  const char *pszOwner)
449
21.5k
{
450
    // May fix https://github.com/OSGeo/gdal/issues/4318
451
21.5k
    if (bInDestruction)
452
0
        return;
453
454
21.5k
    GDALProxyPoolCacheEntry *cur = firstEntry;
455
21.5k
    GIntBig responsiblePID = GDALGetResponsiblePIDForCurrentThread();
456
457
21.5k
    const std::string osFilenameAndOO =
458
21.5k
        GetFilenameAndOpenOptions(pszFileName, papszOpenOptions);
459
460
977k
    while (cur)
461
967k
    {
462
967k
        GDALProxyPoolCacheEntry *next = cur->next;
463
464
967k
        if (cur->refCount == 0 && cur->pszFileNameAndOpenOptions &&
465
967k
            osFilenameAndOO == cur->pszFileNameAndOpenOptions &&
466
967k
            ((pszOwner == nullptr && cur->pszOwner == nullptr) ||
467
26.4k
             (pszOwner != nullptr && cur->pszOwner != nullptr &&
468
26.4k
              strcmp(cur->pszOwner, pszOwner) == 0)) &&
469
967k
            cur->poDS != nullptr)
470
11.1k
        {
471
            /* Close by pretending we are the thread that GDALOpen'ed this */
472
            /* dataset */
473
11.1k
            GDALSetResponsiblePIDForCurrentThread(cur->responsiblePID);
474
475
11.1k
            GDALDataset *poDS = cur->poDS;
476
477
11.1k
            nRAMUsage -= cur->nRAMUsage;
478
11.1k
            cur->nRAMUsage = 0;
479
480
11.1k
            cur->poDS = nullptr;
481
11.1k
            CPLFree(cur->pszFileNameAndOpenOptions);
482
11.1k
            cur->pszFileNameAndOpenOptions = nullptr;
483
11.1k
            CPLFree(cur->pszOwner);
484
11.1k
            cur->pszOwner = nullptr;
485
486
11.1k
            refCountOfDisabledRefCount++;
487
11.1k
            GDALClose(poDS);
488
11.1k
            refCountOfDisabledRefCount--;
489
490
11.1k
            GDALSetResponsiblePIDForCurrentThread(responsiblePID);
491
11.1k
            break;
492
11.1k
        }
493
494
956k
        cur = next;
495
956k
    }
496
21.5k
}
497
498
/************************************************************************/
499
/*                       GDALGetMaxDatasetPoolSize()                    */
500
/************************************************************************/
501
502
/** Return the maximum number of datasets simultaneously opened in the
503
 * dataset pool.
504
 */
505
int GDALGetMaxDatasetPoolSize()
506
880
{
507
880
    int nSize = atoi(CPLGetConfigOption("GDAL_MAX_DATASET_POOL_SIZE", "100"));
508
880
    if (nSize < 2)
509
0
        nSize = 2;
510
880
    else if (nSize > 1000)
511
0
        nSize = 1000;
512
880
    return nSize;
513
880
}
514
515
/************************************************************************/
516
/*                                 Ref()                                */
517
/************************************************************************/
518
519
void GDALDatasetPool::Ref()
520
21.5k
{
521
21.5k
    CPLMutexHolderD(GDALGetphDLMutex());
522
21.5k
    if (singleton == nullptr)
523
880
    {
524
525
        // Try to not consume more than 25% of the usable RAM
526
880
        GIntBig l_nMaxRAMUsage =
527
880
            (CPLGetUsablePhysicalRAM() - GDALGetCacheMax64()) / 4;
528
880
        const char *pszMaxRAMUsage =
529
880
            CPLGetConfigOption("GDAL_MAX_DATASET_POOL_RAM_USAGE", nullptr);
530
880
        if (pszMaxRAMUsage)
531
0
        {
532
0
            l_nMaxRAMUsage = std::strtoll(pszMaxRAMUsage, nullptr, 10);
533
0
            if (strstr(pszMaxRAMUsage, "MB"))
534
0
                l_nMaxRAMUsage *= 1024 * 1024;
535
0
            else if (strstr(pszMaxRAMUsage, "GB"))
536
0
                l_nMaxRAMUsage *= 1024 * 1024 * 1024;
537
0
        }
538
539
880
        singleton =
540
880
            new GDALDatasetPool(GDALGetMaxDatasetPoolSize(), l_nMaxRAMUsage);
541
880
    }
542
21.5k
    if (refCountOfDisabledRefCount == 0)
543
21.5k
        singleton->refCount++;
544
21.5k
}
545
546
/* keep that in sync with gdaldrivermanager.cpp */
547
void GDALDatasetPool::PreventDestroy()
548
0
{
549
0
    CPLMutexHolderD(GDALGetphDLMutex());
550
0
    if (!singleton)
551
0
        return;
552
0
    refCountOfDisabledRefCount++;
553
0
}
554
555
/* keep that in sync with gdaldrivermanager.cpp */
556
extern void GDALDatasetPoolPreventDestroy();
557
558
void GDALDatasetPoolPreventDestroy()
559
0
{
560
0
    GDALDatasetPool::PreventDestroy();
561
0
}
562
563
/************************************************************************/
564
/*                               Unref()                                */
565
/************************************************************************/
566
567
void GDALDatasetPool::Unref()
568
21.5k
{
569
21.5k
    CPLMutexHolderD(GDALGetphDLMutex());
570
21.5k
    if (!singleton)
571
0
    {
572
0
        CPLAssert(false);
573
0
        return;
574
0
    }
575
21.5k
    if (refCountOfDisabledRefCount == 0)
576
21.3k
    {
577
21.3k
        singleton->refCount--;
578
21.3k
        if (singleton->refCount == 0)
579
877
        {
580
877
            delete singleton;
581
877
            singleton = nullptr;
582
877
        }
583
21.3k
    }
584
21.5k
}
585
586
/* keep that in sync with gdaldrivermanager.cpp */
587
void GDALDatasetPool::ForceDestroy()
588
0
{
589
0
    CPLMutexHolderD(GDALGetphDLMutex());
590
0
    if (!singleton)
591
0
        return;
592
0
    refCountOfDisabledRefCount--;
593
0
    CPLAssert(refCountOfDisabledRefCount == 0);
594
0
    singleton->refCount = 0;
595
0
    delete singleton;
596
0
    singleton = nullptr;
597
0
}
598
599
/* keep that in sync with gdaldrivermanager.cpp */
600
extern void GDALDatasetPoolForceDestroy();
601
602
void GDALDatasetPoolForceDestroy()
603
0
{
604
0
    GDALDatasetPool::ForceDestroy();
605
0
}
606
607
/************************************************************************/
608
/*                           RefDataset()                               */
609
/************************************************************************/
610
611
GDALProxyPoolCacheEntry *
612
GDALDatasetPool::RefDataset(const char *pszFileName, GDALAccess eAccess,
613
                            char **papszOpenOptions, int bShared,
614
                            bool bForceOpen, const char *pszOwner)
615
81.9k
{
616
81.9k
    return singleton->_RefDataset(pszFileName, eAccess, papszOpenOptions,
617
81.9k
                                  bShared, bForceOpen, pszOwner);
618
81.9k
}
619
620
/************************************************************************/
621
/*                       UnrefDataset()                                 */
622
/************************************************************************/
623
624
void GDALDatasetPool::UnrefDataset(GDALProxyPoolCacheEntry *cacheEntry)
625
81.8k
{
626
81.8k
    CPLMutexHolderD(GDALGetphDLMutex());
627
81.8k
    cacheEntry->refCount--;
628
81.8k
}
629
630
/************************************************************************/
631
/*                   CloseDatasetIfZeroRefCount()                       */
632
/************************************************************************/
633
634
void GDALDatasetPool::CloseDatasetIfZeroRefCount(const char *pszFileName,
635
                                                 CSLConstList papszOpenOptions,
636
                                                 GDALAccess eAccess,
637
                                                 const char *pszOwner)
638
21.5k
{
639
21.5k
    CPLMutexHolderD(GDALGetphDLMutex());
640
21.5k
    singleton->_CloseDatasetIfZeroRefCount(pszFileName, papszOpenOptions,
641
21.5k
                                           eAccess, pszOwner);
642
21.5k
}
643
644
struct GetMetadataElt
645
{
646
    char *pszDomain;
647
    char **papszMetadata;
648
};
649
650
static unsigned long hash_func_get_metadata(const void *_elt)
651
0
{
652
0
    const GetMetadataElt *elt = static_cast<const GetMetadataElt *>(_elt);
653
0
    return CPLHashSetHashStr(elt->pszDomain);
654
0
}
655
656
static int equal_func_get_metadata(const void *_elt1, const void *_elt2)
657
0
{
658
0
    const GetMetadataElt *elt1 = static_cast<const GetMetadataElt *>(_elt1);
659
0
    const GetMetadataElt *elt2 = static_cast<const GetMetadataElt *>(_elt2);
660
0
    return CPLHashSetEqualStr(elt1->pszDomain, elt2->pszDomain);
661
0
}
662
663
static void free_func_get_metadata(void *_elt)
664
0
{
665
0
    GetMetadataElt *elt = static_cast<GetMetadataElt *>(_elt);
666
0
    CPLFree(elt->pszDomain);
667
0
    CSLDestroy(elt->papszMetadata);
668
0
    CPLFree(elt);
669
0
}
670
671
struct GetMetadataItemElt
672
{
673
    char *pszName;
674
    char *pszDomain;
675
    char *pszMetadataItem;
676
};
677
678
static unsigned long hash_func_get_metadata_item(const void *_elt)
679
947
{
680
947
    const GetMetadataItemElt *elt =
681
947
        static_cast<const GetMetadataItemElt *>(_elt);
682
947
    return CPLHashSetHashStr(elt->pszName) ^ CPLHashSetHashStr(elt->pszDomain);
683
947
}
684
685
static int equal_func_get_metadata_item(const void *_elt1, const void *_elt2)
686
689
{
687
689
    const GetMetadataItemElt *elt1 =
688
689
        static_cast<const GetMetadataItemElt *>(_elt1);
689
689
    const GetMetadataItemElt *elt2 =
690
689
        static_cast<const GetMetadataItemElt *>(_elt2);
691
689
    return CPLHashSetEqualStr(elt1->pszName, elt2->pszName) &&
692
689
           CPLHashSetEqualStr(elt1->pszDomain, elt2->pszDomain);
693
689
}
694
695
static void free_func_get_metadata_item(void *_elt)
696
818
{
697
818
    GetMetadataItemElt *elt = static_cast<GetMetadataItemElt *>(_elt);
698
818
    CPLFree(elt->pszName);
699
818
    CPLFree(elt->pszDomain);
700
818
    CPLFree(elt->pszMetadataItem);
701
818
    CPLFree(elt);
702
818
}
703
704
/* ******************************************************************** */
705
/*                     GDALProxyPoolDataset                             */
706
/* ******************************************************************** */
707
708
/* Note : the bShared parameter must be used with caution. You can */
709
/* set it to TRUE  for being used as a VRT source : in that case, */
710
/* VRTSimpleSource will take care of destroying it when there are no */
711
/* reference to it (in VRTSimpleSource::~VRTSimpleSource()) */
712
/* However this will not be registered as a genuine shared dataset, like it */
713
/* would have been with MarkAsShared(). But MarkAsShared() is not usable for */
714
/* GDALProxyPoolDataset objects, as they share the same description as their */
715
/* underlying dataset. So *NEVER* call MarkAsShared() on a GDALProxyPoolDataset
716
 */
717
/* object */
718
719
/* pszOwner is only honoured in the bShared case, and restrict the scope */
720
/* of the sharing. Only calls to _RefDataset() with the same value of */
721
/* pszOwner can effectively use the same dataset. The use case is */
722
/* to avoid 2 VRTs (potentially the same one) opened by a single thread,
723
 * pointing to */
724
/* the same source datasets. In that case, they would use the same dataset */
725
/* So even if the VRT handles themselves are used from different threads, since
726
 */
727
/* the underlying sources are shared, that might cause crashes (#6939). */
728
/* But we want to allow a same VRT referencing the same source dataset,*/
729
/* for example if it has multiple bands. So in practice the value of pszOwner */
730
/* is the serialized value (%p formatting) of the VRT dataset handle. */
731
732
GDALProxyPoolDataset::GDALProxyPoolDataset(
733
    const char *pszSourceDatasetDescription, int nRasterXSizeIn,
734
    int nRasterYSizeIn, GDALAccess eAccessIn, int bSharedIn,
735
    const char *pszProjectionRefIn, const GDALGeoTransform *pGT,
736
    const char *pszOwner)
737
0
    : responsiblePID(GDALGetResponsiblePIDForCurrentThread()),
738
0
      pszProjectionRef(pszProjectionRefIn ? CPLStrdup(pszProjectionRefIn)
739
0
                                          : nullptr)
740
0
{
741
0
    GDALDatasetPool::Ref();
742
743
0
    SetDescription(pszSourceDatasetDescription);
744
745
0
    nRasterXSize = nRasterXSizeIn;
746
0
    nRasterYSize = nRasterYSizeIn;
747
0
    eAccess = eAccessIn;
748
749
0
    bShared = CPL_TO_BOOL(bSharedIn);
750
0
    m_pszOwner = pszOwner ? CPLStrdup(pszOwner) : nullptr;
751
752
0
    if (pGT)
753
0
    {
754
0
        m_gt = *pGT;
755
0
        m_bHasSrcGeoTransform = true;
756
0
    }
757
758
0
    if (pszProjectionRefIn)
759
0
    {
760
0
        m_poSRS = new OGRSpatialReference();
761
0
        m_poSRS->importFromWkt(pszProjectionRefIn);
762
0
        m_bHasSrcSRS = true;
763
0
    }
764
0
}
765
766
/* Constructor where the parameters (raster size, etc.) are obtained
767
 * by opening the underlying dataset.
768
 */
769
GDALProxyPoolDataset::GDALProxyPoolDataset(
770
    const char *pszSourceDatasetDescription, GDALAccess eAccessIn,
771
    int bSharedIn, const char *pszOwner)
772
21.5k
    : responsiblePID(GDALGetResponsiblePIDForCurrentThread())
773
21.5k
{
774
21.5k
    GDALDatasetPool::Ref();
775
776
21.5k
    SetDescription(pszSourceDatasetDescription);
777
778
21.5k
    eAccess = eAccessIn;
779
780
21.5k
    bShared = CPL_TO_BOOL(bSharedIn);
781
21.5k
    m_pszOwner = pszOwner ? CPLStrdup(pszOwner) : nullptr;
782
21.5k
}
783
784
/************************************************************************/
785
/*                              Create()                                */
786
/************************************************************************/
787
788
/* Instantiate a GDALProxyPoolDataset where the parameters (raster size, etc.)
789
 * are obtained by opening the underlying dataset.
790
 * Its bands are also instantiated.
791
 */
792
GDALProxyPoolDataset *GDALProxyPoolDataset::Create(
793
    const char *pszSourceDatasetDescription, CSLConstList papszOpenOptionsIn,
794
    GDALAccess eAccessIn, int bSharedIn, const char *pszOwner)
795
21.5k
{
796
21.5k
    std::unique_ptr<GDALProxyPoolDataset> poSelf(new GDALProxyPoolDataset(
797
21.5k
        pszSourceDatasetDescription, eAccessIn, bSharedIn, pszOwner));
798
21.5k
    poSelf->SetOpenOptions(papszOpenOptionsIn);
799
21.5k
    GDALDataset *poUnderlyingDS = poSelf->RefUnderlyingDataset();
800
21.5k
    if (!poUnderlyingDS)
801
10.2k
        return nullptr;
802
11.3k
    poSelf->nRasterXSize = poUnderlyingDS->GetRasterXSize();
803
11.3k
    poSelf->nRasterYSize = poUnderlyingDS->GetRasterYSize();
804
11.3k
    if (poUnderlyingDS->GetGeoTransform(poSelf->m_gt) == CE_None)
805
3.62k
        poSelf->m_bHasSrcGeoTransform = true;
806
11.3k
    const auto poSRS = poUnderlyingDS->GetSpatialRef();
807
11.3k
    if (poSRS)
808
3.70k
    {
809
3.70k
        poSelf->m_poSRS = poSRS->Clone();
810
3.70k
        poSelf->m_bHasSrcSRS = true;
811
3.70k
    }
812
58.9k
    for (int i = 1; i <= poUnderlyingDS->GetRasterCount(); ++i)
813
47.6k
    {
814
47.6k
        auto poSrcBand = poUnderlyingDS->GetRasterBand(i);
815
47.6k
        if (!poSrcBand)
816
0
        {
817
0
            poSelf->UnrefUnderlyingDataset(poUnderlyingDS);
818
0
            return nullptr;
819
0
        }
820
47.6k
        int nSrcBlockXSize, nSrcBlockYSize;
821
47.6k
        poSrcBand->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
822
47.6k
        poSelf->AddSrcBandDescription(poSrcBand->GetRasterDataType(),
823
47.6k
                                      nSrcBlockXSize, nSrcBlockYSize);
824
47.6k
    }
825
11.3k
    poSelf->UnrefUnderlyingDataset(poUnderlyingDS);
826
11.3k
    return poSelf.release();
827
11.3k
}
828
829
/************************************************************************/
830
/*                    ~GDALProxyPoolDataset()                           */
831
/************************************************************************/
832
833
GDALProxyPoolDataset::~GDALProxyPoolDataset()
834
21.5k
{
835
21.5k
    GDALDatasetPool::CloseDatasetIfZeroRefCount(
836
21.5k
        GetDescription(), papszOpenOptions, eAccess, m_pszOwner);
837
838
    /* See comment in constructor */
839
    /* It is not really a genuine shared dataset, so we don't */
840
    /* want ~GDALDataset() to try to release it from its */
841
    /* shared dataset hashset. This will save a */
842
    /* "Should not happen. Cannot find %s, this=%p in phSharedDatasetSet" debug
843
     * message */
844
21.5k
    bShared = false;
845
846
21.5k
    CPLFree(pszProjectionRef);
847
21.5k
    CPLFree(pszGCPProjection);
848
21.5k
    if (nGCPCount)
849
0
    {
850
0
        GDALDeinitGCPs(nGCPCount, pasGCPList);
851
0
        CPLFree(pasGCPList);
852
0
    }
853
21.5k
    if (metadataSet)
854
0
        CPLHashSetDestroy(metadataSet);
855
21.5k
    if (metadataItemSet)
856
0
        CPLHashSetDestroy(metadataItemSet);
857
21.5k
    CPLFree(m_pszOwner);
858
21.5k
    if (m_poSRS)
859
3.70k
        m_poSRS->Release();
860
21.5k
    if (m_poGCPSRS)
861
0
        m_poGCPSRS->Release();
862
863
21.5k
    GDALDatasetPool::Unref();
864
21.5k
}
865
866
/************************************************************************/
867
/*                        SetOpenOptions()                              */
868
/************************************************************************/
869
870
void GDALProxyPoolDataset::SetOpenOptions(CSLConstList papszOpenOptionsIn)
871
21.5k
{
872
21.5k
    CPLAssert(papszOpenOptions == nullptr);
873
21.5k
    papszOpenOptions = CSLDuplicate(papszOpenOptionsIn);
874
21.5k
}
875
876
/************************************************************************/
877
/*                    AddSrcBandDescription()                           */
878
/************************************************************************/
879
880
void GDALProxyPoolDataset::AddSrcBandDescription(GDALDataType eDataType,
881
                                                 int nBlockXSize,
882
                                                 int nBlockYSize)
883
47.6k
{
884
47.6k
    SetBand(nBands + 1, new GDALProxyPoolRasterBand(this, nBands + 1, eDataType,
885
47.6k
                                                    nBlockXSize, nBlockYSize));
886
47.6k
}
887
888
/************************************************************************/
889
/*                    AddSrcBand()                                      */
890
/************************************************************************/
891
892
void GDALProxyPoolDataset::AddSrcBand(int nBand, GDALDataType eDataType,
893
                                      int nBlockXSize, int nBlockYSize)
894
0
{
895
0
    SetBand(nBand, new GDALProxyPoolRasterBand(this, nBand, eDataType,
896
0
                                               nBlockXSize, nBlockYSize));
897
0
}
898
899
/************************************************************************/
900
/*                    RefUnderlyingDataset()                            */
901
/************************************************************************/
902
903
GDALDataset *GDALProxyPoolDataset::RefUnderlyingDataset() const
904
21.5k
{
905
21.5k
    return RefUnderlyingDataset(true);
906
21.5k
}
907
908
GDALDataset *GDALProxyPoolDataset::RefUnderlyingDataset(bool bForceOpen) const
909
81.9k
{
910
    /* We pretend that the current thread is responsiblePID, that is */
911
    /* to say the thread that created that GDALProxyPoolDataset object. */
912
    /* This is for the case when a GDALProxyPoolDataset is created by a */
913
    /* thread and used by other threads. These other threads, when doing actual
914
     */
915
    /* IO, will come there and potentially open the underlying dataset. */
916
    /* By doing this, they can indirectly call GDALOpenShared() on .aux file */
917
    /* for example. So this call to GDALOpenShared() must occur as if it */
918
    /* was done by the creating thread, otherwise it will not be correctly
919
     * closed afterwards... */
920
    /* To make a long story short : this is necessary when warping with
921
     * ChunkAndWarpMulti */
922
    /* a VRT of GeoTIFFs that have associated .aux files */
923
81.9k
    GIntBig curResponsiblePID = GDALGetResponsiblePIDForCurrentThread();
924
81.9k
    GDALSetResponsiblePIDForCurrentThread(responsiblePID);
925
81.9k
    cacheEntry =
926
81.9k
        GDALDatasetPool::RefDataset(GetDescription(), eAccess, papszOpenOptions,
927
81.9k
                                    GetShared(), bForceOpen, m_pszOwner);
928
81.9k
    GDALSetResponsiblePIDForCurrentThread(curResponsiblePID);
929
81.9k
    if (cacheEntry != nullptr)
930
81.8k
    {
931
81.8k
        if (cacheEntry->poDS != nullptr)
932
71.6k
            return cacheEntry->poDS;
933
10.2k
        else
934
10.2k
            GDALDatasetPool::UnrefDataset(cacheEntry);
935
81.8k
    }
936
10.2k
    return nullptr;
937
81.9k
}
938
939
/************************************************************************/
940
/*                    UnrefUnderlyingDataset()                        */
941
/************************************************************************/
942
943
void GDALProxyPoolDataset::UnrefUnderlyingDataset(
944
    CPL_UNUSED GDALDataset *poUnderlyingDataset) const
945
71.6k
{
946
71.6k
    if (cacheEntry != nullptr)
947
71.6k
    {
948
71.6k
        CPLAssert(cacheEntry->poDS == poUnderlyingDataset);
949
71.6k
        if (cacheEntry->poDS != nullptr)
950
71.6k
            GDALDatasetPool::UnrefDataset(cacheEntry);
951
71.6k
    }
952
71.6k
}
953
954
/************************************************************************/
955
/*                         FlushCache()                                 */
956
/************************************************************************/
957
958
CPLErr GDALProxyPoolDataset::FlushCache(bool bAtClosing)
959
0
{
960
0
    CPLErr eErr = CE_None;
961
0
    GDALDataset *poUnderlyingDataset = RefUnderlyingDataset(false);
962
0
    if (poUnderlyingDataset)
963
0
    {
964
0
        eErr = poUnderlyingDataset->FlushCache(bAtClosing);
965
0
        UnrefUnderlyingDataset(poUnderlyingDataset);
966
0
    }
967
0
    return eErr;
968
0
}
969
970
/************************************************************************/
971
/*                        SetSpatialRef()                               */
972
/************************************************************************/
973
974
CPLErr GDALProxyPoolDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
975
0
{
976
0
    m_bHasSrcSRS = false;
977
0
    return GDALProxyDataset::SetSpatialRef(poSRS);
978
0
}
979
980
/************************************************************************/
981
/*                        GetSpatialRef()                               */
982
/************************************************************************/
983
984
const OGRSpatialReference *GDALProxyPoolDataset::GetSpatialRef() const
985
0
{
986
0
    if (m_bHasSrcSRS)
987
0
        return m_poSRS;
988
0
    else
989
0
    {
990
0
        if (m_poSRS)
991
0
            m_poSRS->Release();
992
0
        m_poSRS = nullptr;
993
0
        auto poSRS = GDALProxyDataset::GetSpatialRef();
994
0
        if (poSRS)
995
0
            m_poSRS = poSRS->Clone();
996
0
        return m_poSRS;
997
0
    }
998
0
}
999
1000
/************************************************************************/
1001
/*                        SetGeoTransform()                             */
1002
/************************************************************************/
1003
1004
CPLErr GDALProxyPoolDataset::SetGeoTransform(const GDALGeoTransform &gt)
1005
0
{
1006
0
    m_gt = gt;
1007
0
    m_bHasSrcGeoTransform = false;
1008
0
    return GDALProxyDataset::SetGeoTransform(gt);
1009
0
}
1010
1011
/************************************************************************/
1012
/*                        GetGeoTransform()                             */
1013
/************************************************************************/
1014
1015
CPLErr GDALProxyPoolDataset::GetGeoTransform(GDALGeoTransform &gt) const
1016
0
{
1017
0
    if (m_bHasSrcGeoTransform)
1018
0
    {
1019
0
        gt = m_gt;
1020
0
        return CE_None;
1021
0
    }
1022
0
    else
1023
0
    {
1024
0
        return GDALProxyDataset::GetGeoTransform(gt);
1025
0
    }
1026
0
}
1027
1028
/************************************************************************/
1029
/*                            GetMetadata()                             */
1030
/************************************************************************/
1031
1032
char **GDALProxyPoolDataset::GetMetadata(const char *pszDomain)
1033
0
{
1034
0
    if (metadataSet == nullptr)
1035
0
        metadataSet =
1036
0
            CPLHashSetNew(hash_func_get_metadata, equal_func_get_metadata,
1037
0
                          free_func_get_metadata);
1038
1039
0
    GDALDataset *poUnderlyingDataset = RefUnderlyingDataset();
1040
0
    if (poUnderlyingDataset == nullptr)
1041
0
        return nullptr;
1042
1043
0
    char **papszUnderlyingMetadata =
1044
0
        poUnderlyingDataset->GetMetadata(pszDomain);
1045
1046
0
    GetMetadataElt *pElt =
1047
0
        static_cast<GetMetadataElt *>(CPLMalloc(sizeof(GetMetadataElt)));
1048
0
    pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : nullptr;
1049
0
    pElt->papszMetadata = CSLDuplicate(papszUnderlyingMetadata);
1050
0
    CPLHashSetInsert(metadataSet, pElt);
1051
1052
0
    UnrefUnderlyingDataset(poUnderlyingDataset);
1053
1054
0
    return pElt->papszMetadata;
1055
0
}
1056
1057
/************************************************************************/
1058
/*                        GetMetadataItem()                             */
1059
/************************************************************************/
1060
1061
const char *GDALProxyPoolDataset::GetMetadataItem(const char *pszName,
1062
                                                  const char *pszDomain)
1063
0
{
1064
0
    if (metadataItemSet == nullptr)
1065
0
        metadataItemSet = CPLHashSetNew(hash_func_get_metadata_item,
1066
0
                                        equal_func_get_metadata_item,
1067
0
                                        free_func_get_metadata_item);
1068
1069
0
    GDALDataset *poUnderlyingDataset = RefUnderlyingDataset();
1070
0
    if (poUnderlyingDataset == nullptr)
1071
0
        return nullptr;
1072
1073
0
    const char *pszUnderlyingMetadataItem =
1074
0
        poUnderlyingDataset->GetMetadataItem(pszName, pszDomain);
1075
1076
0
    GetMetadataItemElt *pElt = static_cast<GetMetadataItemElt *>(
1077
0
        CPLMalloc(sizeof(GetMetadataItemElt)));
1078
0
    pElt->pszName = (pszName) ? CPLStrdup(pszName) : nullptr;
1079
0
    pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : nullptr;
1080
0
    pElt->pszMetadataItem = (pszUnderlyingMetadataItem)
1081
0
                                ? CPLStrdup(pszUnderlyingMetadataItem)
1082
0
                                : nullptr;
1083
0
    CPLHashSetInsert(metadataItemSet, pElt);
1084
1085
0
    UnrefUnderlyingDataset(poUnderlyingDataset);
1086
1087
0
    return pElt->pszMetadataItem;
1088
0
}
1089
1090
/************************************************************************/
1091
/*                      GetInternalHandle()                             */
1092
/************************************************************************/
1093
1094
void *GDALProxyPoolDataset::GetInternalHandle(const char *pszRequest)
1095
0
{
1096
0
    CPLError(
1097
0
        CE_Warning, CPLE_AppDefined,
1098
0
        "GetInternalHandle() cannot be safely called on a proxy pool dataset\n"
1099
0
        "as the returned value may be invalidated at any time.\n");
1100
0
    return GDALProxyDataset::GetInternalHandle(pszRequest);
1101
0
}
1102
1103
/************************************************************************/
1104
/*                     GetGCPSpatialRef()                               */
1105
/************************************************************************/
1106
1107
const OGRSpatialReference *GDALProxyPoolDataset::GetGCPSpatialRef() const
1108
0
{
1109
0
    GDALDataset *poUnderlyingDataset = RefUnderlyingDataset();
1110
0
    if (poUnderlyingDataset == nullptr)
1111
0
        return nullptr;
1112
1113
0
    m_poGCPSRS->Release();
1114
0
    m_poGCPSRS = nullptr;
1115
1116
0
    const auto poUnderlyingGCPSRS = poUnderlyingDataset->GetGCPSpatialRef();
1117
0
    if (poUnderlyingGCPSRS)
1118
0
        m_poGCPSRS = poUnderlyingGCPSRS->Clone();
1119
1120
0
    UnrefUnderlyingDataset(poUnderlyingDataset);
1121
1122
0
    return m_poGCPSRS;
1123
0
}
1124
1125
/************************************************************************/
1126
/*                            GetGCPs()                                 */
1127
/************************************************************************/
1128
1129
const GDAL_GCP *GDALProxyPoolDataset::GetGCPs()
1130
0
{
1131
0
    GDALDataset *poUnderlyingDataset = RefUnderlyingDataset();
1132
0
    if (poUnderlyingDataset == nullptr)
1133
0
        return nullptr;
1134
1135
0
    if (nGCPCount)
1136
0
    {
1137
0
        GDALDeinitGCPs(nGCPCount, pasGCPList);
1138
0
        CPLFree(pasGCPList);
1139
0
        pasGCPList = nullptr;
1140
0
    }
1141
1142
0
    const GDAL_GCP *pasUnderlyingGCPList = poUnderlyingDataset->GetGCPs();
1143
0
    nGCPCount = poUnderlyingDataset->GetGCPCount();
1144
0
    if (nGCPCount)
1145
0
        pasGCPList = GDALDuplicateGCPs(nGCPCount, pasUnderlyingGCPList);
1146
1147
0
    UnrefUnderlyingDataset(poUnderlyingDataset);
1148
1149
0
    return pasGCPList;
1150
0
}
1151
1152
/************************************************************************/
1153
/*                     GDALProxyPoolDatasetCreate()                     */
1154
/************************************************************************/
1155
1156
GDALProxyPoolDatasetH GDALProxyPoolDatasetCreate(
1157
    const char *pszSourceDatasetDescription, int nRasterXSize, int nRasterYSize,
1158
    GDALAccess eAccess, int bShared, const char *pszProjectionRef,
1159
    const double *padfGeoTransform)
1160
0
{
1161
0
    return reinterpret_cast<GDALProxyPoolDatasetH>(new GDALProxyPoolDataset(
1162
0
        pszSourceDatasetDescription, nRasterXSize, nRasterYSize, eAccess,
1163
0
        bShared, pszProjectionRef,
1164
0
        reinterpret_cast<const GDALGeoTransform *>(padfGeoTransform)));
1165
0
}
1166
1167
/************************************************************************/
1168
/*                       GDALProxyPoolDatasetDelete()                   */
1169
/************************************************************************/
1170
1171
void GDALProxyPoolDatasetDelete(GDALProxyPoolDatasetH hProxyPoolDataset)
1172
0
{
1173
0
    delete reinterpret_cast<GDALProxyPoolDataset *>(hProxyPoolDataset);
1174
0
}
1175
1176
/************************************************************************/
1177
/*              GDALProxyPoolDatasetAddSrcBandDescription()             */
1178
/************************************************************************/
1179
1180
void GDALProxyPoolDatasetAddSrcBandDescription(
1181
    GDALProxyPoolDatasetH hProxyPoolDataset, GDALDataType eDataType,
1182
    int nBlockXSize, int nBlockYSize)
1183
0
{
1184
0
    reinterpret_cast<GDALProxyPoolDataset *>(hProxyPoolDataset)
1185
0
        ->AddSrcBandDescription(eDataType, nBlockXSize, nBlockYSize);
1186
0
}
1187
1188
/* ******************************************************************** */
1189
/*                    GDALProxyPoolRasterBand()                         */
1190
/* ******************************************************************** */
1191
1192
GDALProxyPoolRasterBand::GDALProxyPoolRasterBand(GDALProxyPoolDataset *poDSIn,
1193
                                                 int nBandIn,
1194
                                                 GDALDataType eDataTypeIn,
1195
                                                 int nBlockXSizeIn,
1196
                                                 int nBlockYSizeIn)
1197
47.6k
{
1198
47.6k
    poDS = poDSIn;
1199
47.6k
    nBand = nBandIn;
1200
47.6k
    eDataType = eDataTypeIn;
1201
47.6k
    nRasterXSize = poDSIn->GetRasterXSize();
1202
47.6k
    nRasterYSize = poDSIn->GetRasterYSize();
1203
47.6k
    nBlockXSize = nBlockXSizeIn;
1204
47.6k
    nBlockYSize = nBlockYSizeIn;
1205
47.6k
}
1206
1207
/* ******************************************************************** */
1208
/*                    GDALProxyPoolRasterBand()                         */
1209
/* ******************************************************************** */
1210
1211
GDALProxyPoolRasterBand::GDALProxyPoolRasterBand(
1212
    GDALProxyPoolDataset *poDSIn, GDALRasterBand *poUnderlyingRasterBand)
1213
47.0k
{
1214
47.0k
    poDS = poDSIn;
1215
47.0k
    nBand = poUnderlyingRasterBand->GetBand();
1216
47.0k
    eDataType = poUnderlyingRasterBand->GetRasterDataType();
1217
47.0k
    nRasterXSize = poUnderlyingRasterBand->GetXSize();
1218
47.0k
    nRasterYSize = poUnderlyingRasterBand->GetYSize();
1219
47.0k
    poUnderlyingRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
1220
47.0k
}
1221
1222
/* ******************************************************************** */
1223
/*                   ~GDALProxyPoolRasterBand()                         */
1224
/* ******************************************************************** */
1225
GDALProxyPoolRasterBand::~GDALProxyPoolRasterBand()
1226
94.6k
{
1227
94.6k
    if (metadataSet)
1228
0
        CPLHashSetDestroy(metadataSet);
1229
94.6k
    if (metadataItemSet)
1230
129
        CPLHashSetDestroy(metadataItemSet);
1231
94.6k
    CPLFree(pszUnitType);
1232
94.6k
    CSLDestroy(papszCategoryNames);
1233
94.6k
    if (poColorTable)
1234
0
        delete poColorTable;
1235
1236
141k
    for (int i = 0; i < nSizeProxyOverviewRasterBand; i++)
1237
47.0k
    {
1238
47.0k
        if (papoProxyOverviewRasterBand[i])
1239
47.0k
            delete papoProxyOverviewRasterBand[i];
1240
47.0k
    }
1241
94.6k
    CPLFree(papoProxyOverviewRasterBand);
1242
94.6k
    if (poProxyMaskBand)
1243
0
        delete poProxyMaskBand;
1244
94.6k
}
1245
1246
/************************************************************************/
1247
/*                AddSrcMaskBandDescriptionFromUnderlying()             */
1248
/************************************************************************/
1249
1250
void GDALProxyPoolRasterBand::AddSrcMaskBandDescriptionFromUnderlying()
1251
0
{
1252
0
    if (poProxyMaskBand != nullptr)
1253
0
        return;
1254
0
    GDALRasterBand *poUnderlyingBand = RefUnderlyingRasterBand();
1255
0
    if (poUnderlyingBand == nullptr)
1256
0
        return;
1257
0
    auto poSrcMaskBand = poUnderlyingBand->GetMaskBand();
1258
0
    int nSrcBlockXSize, nSrcBlockYSize;
1259
0
    poSrcMaskBand->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
1260
0
    poProxyMaskBand = new GDALProxyPoolMaskBand(
1261
0
        cpl::down_cast<GDALProxyPoolDataset *>(poDS), this,
1262
0
        poSrcMaskBand->GetRasterDataType(), nSrcBlockXSize, nSrcBlockYSize);
1263
0
    UnrefUnderlyingRasterBand(poUnderlyingBand);
1264
0
}
1265
1266
/************************************************************************/
1267
/*                 AddSrcMaskBandDescription()                          */
1268
/************************************************************************/
1269
1270
void GDALProxyPoolRasterBand::AddSrcMaskBandDescription(
1271
    GDALDataType eDataTypeIn, int nBlockXSizeIn, int nBlockYSizeIn)
1272
0
{
1273
0
    CPLAssert(poProxyMaskBand == nullptr);
1274
0
    poProxyMaskBand = new GDALProxyPoolMaskBand(
1275
0
        cpl::down_cast<GDALProxyPoolDataset *>(poDS), this, eDataTypeIn,
1276
0
        nBlockXSizeIn, nBlockYSizeIn);
1277
0
}
1278
1279
/************************************************************************/
1280
/*                  RefUnderlyingRasterBand()                           */
1281
/************************************************************************/
1282
1283
GDALRasterBand *
1284
GDALProxyPoolRasterBand::RefUnderlyingRasterBand(bool bForceOpen) const
1285
60.3k
{
1286
60.3k
    GDALDataset *poUnderlyingDataset =
1287
60.3k
        (cpl::down_cast<GDALProxyPoolDataset *>(poDS))
1288
60.3k
            ->RefUnderlyingDataset(bForceOpen);
1289
60.3k
    if (poUnderlyingDataset == nullptr)
1290
21
        return nullptr;
1291
1292
60.3k
    GDALRasterBand *poBand = poUnderlyingDataset->GetRasterBand(nBand);
1293
60.3k
    if (poBand == nullptr)
1294
0
    {
1295
0
        (cpl::down_cast<GDALProxyPoolDataset *>(poDS))
1296
0
            ->UnrefUnderlyingDataset(poUnderlyingDataset);
1297
0
    }
1298
60.3k
    else if (nBlockXSize <= 0 || nBlockYSize <= 0)
1299
0
    {
1300
        // Here we try to load nBlockXSize&nBlockYSize from underlying band
1301
        // but we must guarantee that we will not access directly to
1302
        // nBlockXSize/nBlockYSize before RefUnderlyingRasterBand() is called
1303
0
        int nSrcBlockXSize, nSrcBlockYSize;
1304
0
        poBand->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
1305
0
        const_cast<GDALProxyPoolRasterBand *>(this)->nBlockXSize =
1306
0
            nSrcBlockXSize;
1307
0
        const_cast<GDALProxyPoolRasterBand *>(this)->nBlockYSize =
1308
0
            nSrcBlockYSize;
1309
0
    }
1310
1311
60.3k
    return poBand;
1312
60.3k
}
1313
1314
/************************************************************************/
1315
/*                  UnrefUnderlyingRasterBand()                       */
1316
/************************************************************************/
1317
1318
void GDALProxyPoolRasterBand::UnrefUnderlyingRasterBand(
1319
    GDALRasterBand *poUnderlyingRasterBand) const
1320
60.3k
{
1321
60.3k
    if (poUnderlyingRasterBand)
1322
60.3k
        (cpl::down_cast<GDALProxyPoolDataset *>(poDS))
1323
60.3k
            ->UnrefUnderlyingDataset(poUnderlyingRasterBand->GetDataset());
1324
60.3k
}
1325
1326
/************************************************************************/
1327
/*                             FlushCache()                             */
1328
/************************************************************************/
1329
1330
CPLErr GDALProxyPoolRasterBand::FlushCache(bool bAtClosing)
1331
5.02k
{
1332
5.02k
    GDALRasterBand *poUnderlyingRasterBand = RefUnderlyingRasterBand(false);
1333
5.02k
    if (poUnderlyingRasterBand)
1334
5.00k
    {
1335
5.00k
        CPLErr eErr = poUnderlyingRasterBand->FlushCache(bAtClosing);
1336
5.00k
        UnrefUnderlyingRasterBand(poUnderlyingRasterBand);
1337
5.00k
        return eErr;
1338
5.00k
    }
1339
21
    return CE_None;
1340
5.02k
}
1341
1342
/************************************************************************/
1343
/*                            GetMetadata()                             */
1344
/************************************************************************/
1345
1346
char **GDALProxyPoolRasterBand::GetMetadata(const char *pszDomain)
1347
0
{
1348
0
    if (metadataSet == nullptr)
1349
0
        metadataSet =
1350
0
            CPLHashSetNew(hash_func_get_metadata, equal_func_get_metadata,
1351
0
                          free_func_get_metadata);
1352
1353
0
    GDALRasterBand *poUnderlyingRasterBand = RefUnderlyingRasterBand();
1354
0
    if (poUnderlyingRasterBand == nullptr)
1355
0
        return nullptr;
1356
1357
0
    char **papszUnderlyingMetadata =
1358
0
        poUnderlyingRasterBand->GetMetadata(pszDomain);
1359
1360
0
    GetMetadataElt *pElt =
1361
0
        static_cast<GetMetadataElt *>(CPLMalloc(sizeof(GetMetadataElt)));
1362
0
    pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : nullptr;
1363
0
    pElt->papszMetadata = CSLDuplicate(papszUnderlyingMetadata);
1364
0
    CPLHashSetInsert(metadataSet, pElt);
1365
1366
0
    UnrefUnderlyingRasterBand(poUnderlyingRasterBand);
1367
1368
0
    return pElt->papszMetadata;
1369
0
}
1370
1371
/************************************************************************/
1372
/*                        GetMetadataItem()                             */
1373
/************************************************************************/
1374
1375
const char *GDALProxyPoolRasterBand::GetMetadataItem(const char *pszName,
1376
                                                     const char *pszDomain)
1377
818
{
1378
818
    if (metadataItemSet == nullptr)
1379
129
        metadataItemSet = CPLHashSetNew(hash_func_get_metadata_item,
1380
129
                                        equal_func_get_metadata_item,
1381
129
                                        free_func_get_metadata_item);
1382
1383
818
    GDALRasterBand *poUnderlyingRasterBand = RefUnderlyingRasterBand();
1384
818
    if (poUnderlyingRasterBand == nullptr)
1385
0
        return nullptr;
1386
1387
818
    const char *pszUnderlyingMetadataItem =
1388
818
        poUnderlyingRasterBand->GetMetadataItem(pszName, pszDomain);
1389
1390
818
    GetMetadataItemElt *pElt = static_cast<GetMetadataItemElt *>(
1391
818
        CPLMalloc(sizeof(GetMetadataItemElt)));
1392
818
    pElt->pszName = (pszName) ? CPLStrdup(pszName) : nullptr;
1393
818
    pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : nullptr;
1394
818
    pElt->pszMetadataItem = (pszUnderlyingMetadataItem)
1395
818
                                ? CPLStrdup(pszUnderlyingMetadataItem)
1396
818
                                : nullptr;
1397
818
    CPLHashSetInsert(metadataItemSet, pElt);
1398
1399
818
    UnrefUnderlyingRasterBand(poUnderlyingRasterBand);
1400
1401
818
    return pElt->pszMetadataItem;
1402
818
}
1403
1404
/* ******************************************************************** */
1405
/*                       GetCategoryNames()                             */
1406
/* ******************************************************************** */
1407
1408
char **GDALProxyPoolRasterBand::GetCategoryNames()
1409
0
{
1410
0
    GDALRasterBand *poUnderlyingRasterBand = RefUnderlyingRasterBand();
1411
0
    if (poUnderlyingRasterBand == nullptr)
1412
0
        return nullptr;
1413
1414
0
    CSLDestroy(papszCategoryNames);
1415
0
    papszCategoryNames = nullptr;
1416
1417
0
    char **papszUnderlyingCategoryNames =
1418
0
        poUnderlyingRasterBand->GetCategoryNames();
1419
0
    if (papszUnderlyingCategoryNames)
1420
0
        papszCategoryNames = CSLDuplicate(papszUnderlyingCategoryNames);
1421
1422
0
    UnrefUnderlyingRasterBand(poUnderlyingRasterBand);
1423
1424
0
    return papszCategoryNames;
1425
0
}
1426
1427
/* ******************************************************************** */
1428
/*                           GetUnitType()                              */
1429
/* ******************************************************************** */
1430
1431
const char *GDALProxyPoolRasterBand::GetUnitType()
1432
0
{
1433
0
    GDALRasterBand *poUnderlyingRasterBand = RefUnderlyingRasterBand();
1434
0
    if (poUnderlyingRasterBand == nullptr)
1435
0
        return nullptr;
1436
1437
0
    CPLFree(pszUnitType);
1438
0
    pszUnitType = nullptr;
1439
1440
0
    const char *pszUnderlyingUnitType = poUnderlyingRasterBand->GetUnitType();
1441
0
    if (pszUnderlyingUnitType)
1442
0
        pszUnitType = CPLStrdup(pszUnderlyingUnitType);
1443
1444
0
    UnrefUnderlyingRasterBand(poUnderlyingRasterBand);
1445
1446
0
    return pszUnitType;
1447
0
}
1448
1449
/* ******************************************************************** */
1450
/*                          GetColorTable()                             */
1451
/* ******************************************************************** */
1452
1453
GDALColorTable *GDALProxyPoolRasterBand::GetColorTable()
1454
0
{
1455
0
    GDALRasterBand *poUnderlyingRasterBand = RefUnderlyingRasterBand();
1456
0
    if (poUnderlyingRasterBand == nullptr)
1457
0
        return nullptr;
1458
1459
0
    if (poColorTable)
1460
0
        delete poColorTable;
1461
0
    poColorTable = nullptr;
1462
1463
0
    GDALColorTable *poUnderlyingColorTable =
1464
0
        poUnderlyingRasterBand->GetColorTable();
1465
0
    if (poUnderlyingColorTable)
1466
0
        poColorTable = poUnderlyingColorTable->Clone();
1467
1468
0
    UnrefUnderlyingRasterBand(poUnderlyingRasterBand);
1469
1470
0
    return poColorTable;
1471
0
}
1472
1473
/* ******************************************************************** */
1474
/*                           GetOverview()                              */
1475
/* ******************************************************************** */
1476
1477
GDALRasterBand *GDALProxyPoolRasterBand::GetOverview(int nOverviewBand)
1478
67.8k
{
1479
67.8k
    if (nOverviewBand >= 0 && nOverviewBand < nSizeProxyOverviewRasterBand)
1480
20.7k
    {
1481
20.7k
        if (papoProxyOverviewRasterBand[nOverviewBand])
1482
20.7k
            return papoProxyOverviewRasterBand[nOverviewBand];
1483
20.7k
    }
1484
1485
47.0k
    GDALRasterBand *poUnderlyingRasterBand = RefUnderlyingRasterBand();
1486
47.0k
    if (poUnderlyingRasterBand == nullptr)
1487
0
        return nullptr;
1488
1489
47.0k
    GDALRasterBand *poOverviewRasterBand =
1490
47.0k
        poUnderlyingRasterBand->GetOverview(nOverviewBand);
1491
47.0k
    if (poOverviewRasterBand == nullptr)
1492
0
    {
1493
0
        UnrefUnderlyingRasterBand(poUnderlyingRasterBand);
1494
0
        return nullptr;
1495
0
    }
1496
1497
47.0k
    if (nOverviewBand >= nSizeProxyOverviewRasterBand)
1498
47.0k
    {
1499
47.0k
        papoProxyOverviewRasterBand =
1500
47.0k
            static_cast<GDALProxyPoolOverviewRasterBand **>(
1501
47.0k
                CPLRealloc(papoProxyOverviewRasterBand,
1502
47.0k
                           sizeof(GDALProxyPoolOverviewRasterBand *) *
1503
47.0k
                               (nOverviewBand + 1)));
1504
94.1k
        for (int i = nSizeProxyOverviewRasterBand; i < nOverviewBand + 1; i++)
1505
47.0k
            papoProxyOverviewRasterBand[i] = nullptr;
1506
47.0k
        nSizeProxyOverviewRasterBand = nOverviewBand + 1;
1507
47.0k
    }
1508
1509
47.0k
    papoProxyOverviewRasterBand[nOverviewBand] =
1510
47.0k
        new GDALProxyPoolOverviewRasterBand(
1511
47.0k
            cpl::down_cast<GDALProxyPoolDataset *>(poDS), poOverviewRasterBand,
1512
47.0k
            this, nOverviewBand);
1513
1514
47.0k
    UnrefUnderlyingRasterBand(poUnderlyingRasterBand);
1515
1516
47.0k
    return papoProxyOverviewRasterBand[nOverviewBand];
1517
47.0k
}
1518
1519
/* ******************************************************************** */
1520
/*                     GetRasterSampleOverview()                        */
1521
/* ******************************************************************** */
1522
1523
GDALRasterBand *
1524
GDALProxyPoolRasterBand::GetRasterSampleOverview(GUIntBig /* nDesiredSamples */)
1525
0
{
1526
0
    CPLError(CE_Failure, CPLE_AppDefined,
1527
0
             "GDALProxyPoolRasterBand::GetRasterSampleOverview : not "
1528
0
             "implemented yet");
1529
0
    return nullptr;
1530
0
}
1531
1532
/* ******************************************************************** */
1533
/*                           GetMaskBand()                              */
1534
/* ******************************************************************** */
1535
1536
GDALRasterBand *GDALProxyPoolRasterBand::GetMaskBand()
1537
0
{
1538
0
    if (poProxyMaskBand)
1539
0
        return poProxyMaskBand;
1540
1541
0
    GDALRasterBand *poUnderlyingRasterBand = RefUnderlyingRasterBand();
1542
0
    if (poUnderlyingRasterBand == nullptr)
1543
0
        return nullptr;
1544
1545
0
    GDALRasterBand *poMaskBand = poUnderlyingRasterBand->GetMaskBand();
1546
1547
0
    poProxyMaskBand = new GDALProxyPoolMaskBand(
1548
0
        cpl::down_cast<GDALProxyPoolDataset *>(poDS), poMaskBand, this);
1549
1550
0
    UnrefUnderlyingRasterBand(poUnderlyingRasterBand);
1551
1552
0
    return poProxyMaskBand;
1553
0
}
1554
1555
/* ******************************************************************** */
1556
/*             GDALProxyPoolOverviewRasterBand()                        */
1557
/* ******************************************************************** */
1558
1559
GDALProxyPoolOverviewRasterBand::GDALProxyPoolOverviewRasterBand(
1560
    GDALProxyPoolDataset *poDSIn, GDALRasterBand *poUnderlyingOverviewBand,
1561
    GDALProxyPoolRasterBand *poMainBandIn, int nOverviewBandIn)
1562
47.0k
    : GDALProxyPoolRasterBand(poDSIn, poUnderlyingOverviewBand),
1563
47.0k
      poMainBand(poMainBandIn), nOverviewBand(nOverviewBandIn)
1564
47.0k
{
1565
47.0k
}
1566
1567
/* ******************************************************************** */
1568
/*                  ~GDALProxyPoolOverviewRasterBand()                  */
1569
/* ******************************************************************** */
1570
1571
GDALProxyPoolOverviewRasterBand::~GDALProxyPoolOverviewRasterBand()
1572
47.0k
{
1573
47.0k
    CPLAssert(nRefCountUnderlyingMainRasterBand == 0);
1574
47.0k
}
1575
1576
/* ******************************************************************** */
1577
/*                    RefUnderlyingRasterBand()                         */
1578
/* ******************************************************************** */
1579
1580
GDALRasterBand *
1581
GDALProxyPoolOverviewRasterBand::RefUnderlyingRasterBand(bool bForceOpen) const
1582
0
{
1583
0
    poUnderlyingMainRasterBand =
1584
0
        poMainBand->RefUnderlyingRasterBand(bForceOpen);
1585
0
    if (poUnderlyingMainRasterBand == nullptr)
1586
0
        return nullptr;
1587
1588
0
    nRefCountUnderlyingMainRasterBand++;
1589
0
    return poUnderlyingMainRasterBand->GetOverview(nOverviewBand);
1590
0
}
1591
1592
/* ******************************************************************** */
1593
/*                  UnrefUnderlyingRasterBand()                         */
1594
/* ******************************************************************** */
1595
1596
void GDALProxyPoolOverviewRasterBand::UnrefUnderlyingRasterBand(
1597
    GDALRasterBand * /* poUnderlyingRasterBand */) const
1598
0
{
1599
0
    poMainBand->UnrefUnderlyingRasterBand(poUnderlyingMainRasterBand);
1600
0
    nRefCountUnderlyingMainRasterBand--;
1601
0
}
1602
1603
/* ******************************************************************** */
1604
/*                     GDALProxyPoolMaskBand()                          */
1605
/* ******************************************************************** */
1606
1607
GDALProxyPoolMaskBand::GDALProxyPoolMaskBand(
1608
    GDALProxyPoolDataset *poDSIn, GDALRasterBand *poUnderlyingMaskBand,
1609
    GDALProxyPoolRasterBand *poMainBandIn)
1610
0
    : GDALProxyPoolRasterBand(poDSIn, poUnderlyingMaskBand)
1611
0
{
1612
0
    poMainBand = poMainBandIn;
1613
1614
0
    poUnderlyingMainRasterBand = nullptr;
1615
0
    nRefCountUnderlyingMainRasterBand = 0;
1616
0
}
1617
1618
/* ******************************************************************** */
1619
/*                     GDALProxyPoolMaskBand()                          */
1620
/* ******************************************************************** */
1621
1622
GDALProxyPoolMaskBand::GDALProxyPoolMaskBand(
1623
    GDALProxyPoolDataset *poDSIn, GDALProxyPoolRasterBand *poMainBandIn,
1624
    GDALDataType eDataTypeIn, int nBlockXSizeIn, int nBlockYSizeIn)
1625
0
    : GDALProxyPoolRasterBand(poDSIn, 1, eDataTypeIn, nBlockXSizeIn,
1626
0
                              nBlockYSizeIn),
1627
0
      poMainBand(poMainBandIn)
1628
0
{
1629
0
}
1630
1631
/* ******************************************************************** */
1632
/*                          ~GDALProxyPoolMaskBand()                    */
1633
/* ******************************************************************** */
1634
1635
GDALProxyPoolMaskBand::~GDALProxyPoolMaskBand()
1636
0
{
1637
0
    CPLAssert(nRefCountUnderlyingMainRasterBand == 0);
1638
0
}
1639
1640
/* ******************************************************************** */
1641
/*                    RefUnderlyingRasterBand()                         */
1642
/* ******************************************************************** */
1643
1644
GDALRasterBand *
1645
GDALProxyPoolMaskBand::RefUnderlyingRasterBand(bool bForceOpen) const
1646
0
{
1647
0
    poUnderlyingMainRasterBand =
1648
0
        poMainBand->RefUnderlyingRasterBand(bForceOpen);
1649
0
    if (poUnderlyingMainRasterBand == nullptr)
1650
0
        return nullptr;
1651
1652
0
    nRefCountUnderlyingMainRasterBand++;
1653
0
    return poUnderlyingMainRasterBand->GetMaskBand();
1654
0
}
1655
1656
/* ******************************************************************** */
1657
/*                  UnrefUnderlyingRasterBand()                         */
1658
/* ******************************************************************** */
1659
1660
void GDALProxyPoolMaskBand::UnrefUnderlyingRasterBand(
1661
    GDALRasterBand * /* poUnderlyingRasterBand */) const
1662
0
{
1663
0
    poMainBand->UnrefUnderlyingRasterBand(poUnderlyingMainRasterBand);
1664
0
    nRefCountUnderlyingMainRasterBand--;
1665
0
}
1666
1667
//! @endcond