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