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