/****************************************************************************** * * Project: GDAL Core * Purpose: A dataset and raster band classes that differ the opening of the * underlying dataset in a limited pool of opened datasets. * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "cpl_port.h" #include "gdal_proxy.h" #include #include #include #include "cpl_conv.h" #include "cpl_error.h" #include "cpl_hash_set.h" #include "cpl_multiproc.h" #include "cpl_string.h" #include "gdal.h" #include "gdal_priv.h" //! @cond Doxygen_Suppress CPL_CVSID("$Id: gdalproxypool.cpp 5aa7ae3d46661c107e1138aae376bb0b181d83ab 2021-08-28 19:35:01 +0200 Even Rouault $") /* We *must* share the same mutex as the gdaldataset.cpp file, as we are */ /* doing GDALOpen() calls that can indirectly call GDALOpenShared() on */ /* an auxiliary dataset ... */ /* Then we could get dead-locks in multi-threaded use case */ /* ******************************************************************** */ /* GDALDatasetPool */ /* ******************************************************************** */ /* This class is a singleton that maintains a pool of opened datasets */ /* The cache uses a LRU strategy */ class GDALDatasetPool; static GDALDatasetPool* singleton = nullptr; void GDALNullifyProxyPoolSingleton() { singleton = nullptr; } struct _GDALProxyPoolCacheEntry { GIntBig responsiblePID; char *pszFileName; char *pszOwner; GDALDataset *poDS; /* Ref count of the cached dataset */ int refCount; GDALProxyPoolCacheEntry* prev; GDALProxyPoolCacheEntry* next; }; class GDALDatasetPool { private: bool bInDestruction = false; /* Ref count of the pool singleton */ /* Taken by "toplevel" GDALProxyPoolDataset in its constructor and released */ /* in its destructor. See also refCountOfDisableRefCount for the difference */ /* between toplevel and inner GDALProxyPoolDataset */ int refCount = 0; int maxSize = 0; int currentSize = 0; GDALProxyPoolCacheEntry* firstEntry = nullptr; GDALProxyPoolCacheEntry* lastEntry = nullptr; /* This variable prevents a dataset that is going to be opened in GDALDatasetPool::_RefDataset */ /* from increasing refCount if, during its opening, it creates a GDALProxyPoolDataset */ /* We increment it before opening or closing a cached dataset and decrement it afterwards */ /* The typical use case is a VRT made of simple sources that are VRT */ /* We don't want the "inner" VRT to take a reference on the pool, otherwise there is */ /* a high chance that this reference will not be dropped and the pool remain ghost */ int refCountOfDisableRefCount= 0; /* Caution : to be sure that we don't run out of entries, size must be at */ /* least greater or equal than the maximum number of threads */ explicit GDALDatasetPool(int maxSize); ~GDALDatasetPool(); GDALProxyPoolCacheEntry* _RefDataset(const char* pszFileName, GDALAccess eAccess, char** papszOpenOptions, int bShared, bool bForceOpen, const char* pszOwner); void _CloseDatasetIfZeroRefCount( const char* pszFileName, GDALAccess eAccess, const char* pszOwner); #ifdef DEBUG_PROXY_POOL // cppcheck-suppress unusedPrivateFunction void ShowContent(); void CheckLinks(); #endif CPL_DISALLOW_COPY_ASSIGN(GDALDatasetPool) public: static void Ref(); static void Unref(); static GDALProxyPoolCacheEntry* RefDataset(const char* pszFileName, GDALAccess eAccess, char** papszOpenOptions, int bShared, bool bForceOpen, const char* pszOwner); static void UnrefDataset(GDALProxyPoolCacheEntry* cacheEntry); static void CloseDatasetIfZeroRefCount( const char* pszFileName, GDALAccess eAccess, const char* pszOwner); static void PreventDestroy(); static void ForceDestroy(); }; /************************************************************************/ /* GDALDatasetPool() */ /************************************************************************/ GDALDatasetPool::GDALDatasetPool(int maxSizeIn): maxSize(maxSizeIn) { } /************************************************************************/ /* ~GDALDatasetPool() */ /************************************************************************/ GDALDatasetPool::~GDALDatasetPool() { bInDestruction = true; GDALProxyPoolCacheEntry* cur = firstEntry; GIntBig responsiblePID = GDALGetResponsiblePIDForCurrentThread(); while(cur) { GDALProxyPoolCacheEntry* next = cur->next; CPLFree(cur->pszFileName); CPLFree(cur->pszOwner); CPLAssert(cur->refCount == 0); if (cur->poDS) { GDALSetResponsiblePIDForCurrentThread(cur->responsiblePID); GDALClose(cur->poDS); } CPLFree(cur); cur = next; } GDALSetResponsiblePIDForCurrentThread(responsiblePID); } #ifdef DEBUG_PROXY_POOL /************************************************************************/ /* ShowContent() */ /************************************************************************/ void GDALDatasetPool::ShowContent() { GDALProxyPoolCacheEntry* cur = firstEntry; int i = 0; while(cur) { printf("[%d] pszFileName=%s, owner=%s, refCount=%d, responsiblePID=%d\n",/*ok*/ i, cur->pszFileName, cur->pszOwner ? cur->pszOwner : "(null)", cur->refCount, (int)cur->responsiblePID); i++; cur = cur->next; } } /************************************************************************/ /* CheckLinks() */ /************************************************************************/ void GDALDatasetPool::CheckLinks() { GDALProxyPoolCacheEntry* cur = firstEntry; int i = 0; while(cur) { CPLAssert(cur == firstEntry || cur->prev->next == cur); CPLAssert(cur == lastEntry || cur->next->prev == cur); ++i; CPLAssert(cur->next != nullptr || cur == lastEntry); cur = cur->next; } (void)i; CPLAssert(i == currentSize); } #endif /************************************************************************/ /* _RefDataset() */ /************************************************************************/ GDALProxyPoolCacheEntry* GDALDatasetPool::_RefDataset(const char* pszFileName, GDALAccess eAccess, char** papszOpenOptions, int bShared, bool bForceOpen, const char* pszOwner) { if( bInDestruction ) return nullptr; GDALProxyPoolCacheEntry* cur = firstEntry; GIntBig responsiblePID = GDALGetResponsiblePIDForCurrentThread(); GDALProxyPoolCacheEntry* lastEntryWithZeroRefCount = nullptr; while(cur) { GDALProxyPoolCacheEntry* next = cur->next; if (strcmp(cur->pszFileName, pszFileName) == 0 && ((bShared && cur->responsiblePID == responsiblePID && ((cur->pszOwner == nullptr && pszOwner == nullptr) || (cur->pszOwner != nullptr && pszOwner != nullptr && strcmp(cur->pszOwner, pszOwner) == 0))) || (!bShared && cur->refCount == 0)) ) { if (cur != firstEntry) { /* Move to begin */ if (cur->next) cur->next->prev = cur->prev; else lastEntry = cur->prev; cur->prev->next = cur->next; cur->prev = nullptr; firstEntry->prev = cur; cur->next = firstEntry; firstEntry = cur; #ifdef DEBUG_PROXY_POOL CheckLinks(); #endif } cur->refCount ++; return cur; } if (cur->refCount == 0) lastEntryWithZeroRefCount = cur; cur = next; } if( !bForceOpen ) return nullptr; if (currentSize == maxSize) { if (lastEntryWithZeroRefCount == nullptr) { CPLError(CE_Failure, CPLE_AppDefined, "Too many threads are running for the current value of the dataset pool size (%d).\n" "or too many proxy datasets are opened in a cascaded way.\n" "Try increasing GDAL_MAX_DATASET_POOL_SIZE.", maxSize); return nullptr; } lastEntryWithZeroRefCount->pszFileName[0] = '\0'; if (lastEntryWithZeroRefCount->poDS) { /* Close by pretending we are the thread that GDALOpen'ed this */ /* dataset */ GDALSetResponsiblePIDForCurrentThread(lastEntryWithZeroRefCount->responsiblePID); refCountOfDisableRefCount ++; GDALClose(lastEntryWithZeroRefCount->poDS); refCountOfDisableRefCount --; lastEntryWithZeroRefCount->poDS = nullptr; GDALSetResponsiblePIDForCurrentThread(responsiblePID); } CPLFree(lastEntryWithZeroRefCount->pszFileName); CPLFree(lastEntryWithZeroRefCount->pszOwner); /* Recycle this entry for the to-be-opened dataset and */ /* moves it to the top of the list */ if (lastEntryWithZeroRefCount->prev) lastEntryWithZeroRefCount->prev->next = lastEntryWithZeroRefCount->next; else { CPLAssert(false); } if (lastEntryWithZeroRefCount->next) lastEntryWithZeroRefCount->next->prev = lastEntryWithZeroRefCount->prev; else { CPLAssert(lastEntryWithZeroRefCount == lastEntry); lastEntry->prev->next = nullptr; lastEntry = lastEntry->prev; } lastEntryWithZeroRefCount->prev = nullptr; lastEntryWithZeroRefCount->next = firstEntry; firstEntry->prev = lastEntryWithZeroRefCount; cur = firstEntry = lastEntryWithZeroRefCount; #ifdef DEBUG_PROXY_POOL CheckLinks(); #endif } else { /* Prepend */ cur = static_cast(CPLMalloc(sizeof(GDALProxyPoolCacheEntry))); if (lastEntry == nullptr) lastEntry = cur; cur->prev = nullptr; cur->next = firstEntry; if (firstEntry) firstEntry->prev = cur; firstEntry = cur; currentSize ++; #ifdef DEBUG_PROXY_POOL CheckLinks(); #endif } cur->pszFileName = CPLStrdup(pszFileName); cur->pszOwner = (pszOwner) ? CPLStrdup(pszOwner) : nullptr; cur->responsiblePID = responsiblePID; cur->refCount = 1; refCountOfDisableRefCount ++; int nFlag = ((eAccess == GA_Update) ? GDAL_OF_UPDATE : GDAL_OF_READONLY) | GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR; CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true); cur->poDS = GDALDataset::Open( pszFileName, nFlag, nullptr, papszOpenOptions, nullptr ); refCountOfDisableRefCount --; return cur; } /************************************************************************/ /* _CloseDatasetIfZeroRefCount() */ /************************************************************************/ void GDALDatasetPool::_CloseDatasetIfZeroRefCount( const char* pszFileName, GDALAccess /* eAccess */, const char* pszOwner ) { // May fix https://github.com/OSGeo/gdal/issues/4318 if( bInDestruction ) return; GDALProxyPoolCacheEntry* cur = firstEntry; GIntBig responsiblePID = GDALGetResponsiblePIDForCurrentThread(); while(cur) { GDALProxyPoolCacheEntry* next = cur->next; CPLAssert(cur->pszFileName); if (cur->refCount == 0 && strcmp(cur->pszFileName, pszFileName) == 0 && ((pszOwner == nullptr && cur->pszOwner == nullptr) || (pszOwner != nullptr && cur->pszOwner != nullptr && strcmp(cur->pszOwner, pszOwner) == 0)) && cur->poDS != nullptr ) { /* Close by pretending we are the thread that GDALOpen'ed this */ /* dataset */ GDALSetResponsiblePIDForCurrentThread(cur->responsiblePID); GDALDataset* poDS = cur->poDS; cur->poDS = nullptr; cur->pszFileName[0] = '\0'; CPLFree(cur->pszOwner); cur->pszOwner = nullptr; refCountOfDisableRefCount ++; GDALClose(poDS); refCountOfDisableRefCount --; GDALSetResponsiblePIDForCurrentThread(responsiblePID); break; } cur = next; } } /************************************************************************/ /* Ref() */ /************************************************************************/ void GDALDatasetPool::Ref() { CPLMutexHolderD( GDALGetphDLMutex() ); if (singleton == nullptr) { int l_maxSize = atoi(CPLGetConfigOption("GDAL_MAX_DATASET_POOL_SIZE", "100")); if (l_maxSize < 2 || l_maxSize > 1000) l_maxSize = 100; singleton = new GDALDatasetPool(l_maxSize); } if (singleton->refCountOfDisableRefCount == 0) singleton->refCount++; } /* keep that in sync with gdaldrivermanager.cpp */ void GDALDatasetPool::PreventDestroy() { CPLMutexHolderD( GDALGetphDLMutex() ); if (! singleton) return; singleton->refCountOfDisableRefCount ++; } /* keep that in sync with gdaldrivermanager.cpp */ extern void GDALDatasetPoolPreventDestroy(); void GDALDatasetPoolPreventDestroy() { GDALDatasetPool::PreventDestroy(); } /************************************************************************/ /* Unref() */ /************************************************************************/ void GDALDatasetPool::Unref() { CPLMutexHolderD( GDALGetphDLMutex() ); if (! singleton) { CPLAssert(false); return; } if (singleton->refCountOfDisableRefCount == 0) { singleton->refCount--; if (singleton->refCount == 0) { delete singleton; singleton = nullptr; } } } /* keep that in sync with gdaldrivermanager.cpp */ void GDALDatasetPool::ForceDestroy() { CPLMutexHolderD( GDALGetphDLMutex() ); if (! singleton) return; singleton->refCountOfDisableRefCount --; CPLAssert(singleton->refCountOfDisableRefCount == 0); singleton->refCount = 0; delete singleton; singleton = nullptr; } /* keep that in sync with gdaldrivermanager.cpp */ extern void GDALDatasetPoolForceDestroy(); void GDALDatasetPoolForceDestroy() { GDALDatasetPool::ForceDestroy(); } /************************************************************************/ /* RefDataset() */ /************************************************************************/ GDALProxyPoolCacheEntry* GDALDatasetPool::RefDataset(const char* pszFileName, GDALAccess eAccess, char** papszOpenOptions, int bShared, bool bForceOpen, const char* pszOwner) { CPLMutexHolderD( GDALGetphDLMutex() ); return singleton->_RefDataset(pszFileName, eAccess, papszOpenOptions, bShared, bForceOpen, pszOwner); } /************************************************************************/ /* UnrefDataset() */ /************************************************************************/ void GDALDatasetPool::UnrefDataset(GDALProxyPoolCacheEntry* cacheEntry) { CPLMutexHolderD( GDALGetphDLMutex() ); cacheEntry->refCount --; } /************************************************************************/ /* CloseDatasetIfZeroRefCount() */ /************************************************************************/ void GDALDatasetPool::CloseDatasetIfZeroRefCount( const char* pszFileName, GDALAccess eAccess, const char* pszOwner) { CPLMutexHolderD( GDALGetphDLMutex() ); singleton->_CloseDatasetIfZeroRefCount(pszFileName, eAccess, pszOwner); } struct GetMetadataElt { char* pszDomain; char** papszMetadata; }; static unsigned long hash_func_get_metadata(const void* _elt) { const GetMetadataElt* elt = static_cast(_elt); return CPLHashSetHashStr(elt->pszDomain); } static int equal_func_get_metadata(const void* _elt1, const void* _elt2) { const GetMetadataElt* elt1 = static_cast(_elt1); const GetMetadataElt* elt2 = static_cast(_elt2); return CPLHashSetEqualStr(elt1->pszDomain, elt2->pszDomain); } static void free_func_get_metadata(void* _elt) { GetMetadataElt* elt = static_cast(_elt); CPLFree(elt->pszDomain); CSLDestroy(elt->papszMetadata); CPLFree(elt); } struct GetMetadataItemElt { char* pszName; char* pszDomain; char* pszMetadataItem; }; static unsigned long hash_func_get_metadata_item(const void* _elt) { const GetMetadataItemElt* elt = static_cast(_elt); return CPLHashSetHashStr(elt->pszName) ^ CPLHashSetHashStr(elt->pszDomain); } static int equal_func_get_metadata_item(const void* _elt1, const void* _elt2) { const GetMetadataItemElt* elt1 = static_cast(_elt1); const GetMetadataItemElt* elt2 = static_cast(_elt2); return CPLHashSetEqualStr(elt1->pszName, elt2->pszName) && CPLHashSetEqualStr(elt1->pszDomain, elt2->pszDomain); } static void free_func_get_metadata_item(void* _elt) { GetMetadataItemElt* elt = static_cast(_elt); CPLFree(elt->pszName); CPLFree(elt->pszDomain); CPLFree(elt->pszMetadataItem); CPLFree(elt); } /* ******************************************************************** */ /* GDALProxyPoolDataset */ /* ******************************************************************** */ /* Note : the bShared parameter must be used with caution. You can */ /* set it to TRUE for being used as a VRT source : in that case, */ /* VRTSimpleSource will take care of destroying it when there are no */ /* reference to it (in VRTSimpleSource::~VRTSimpleSource()) */ /* However this will not be registered as a genuine shared dataset, like it */ /* would have been with MarkAsShared(). But MarkAsShared() is not usable for */ /* GDALProxyPoolDataset objects, as they share the same description as their */ /* underlying dataset. So *NEVER* call MarkAsShared() on a GDALProxyPoolDataset */ /* object */ /* pszOwner is only honoured in the bShared case, and restrict the scope */ /* of the sharing. Only calls to _RefDataset() with the same value of */ /* pszOwner can effectively use the same dataset. The use case is */ /* to avoid 2 VRTs (potentially the same one) opened by a single thread, pointing to */ /* the same source datasets. In that case, they would use the same dataset */ /* So even if the VRT handles themselves are used from different threads, since */ /* the underlying sources are shared, that might cause crashes (#6939). */ /* But we want to allow a same VRT referencing the same source dataset,*/ /* for example if it has multiple bands. So in practice the value of pszOwner */ /* is the serialized value (%p formatting) of the VRT dataset handle. */ GDALProxyPoolDataset::GDALProxyPoolDataset(const char* pszSourceDatasetDescription, int nRasterXSizeIn, int nRasterYSizeIn, GDALAccess eAccessIn, int bSharedIn, const char * pszProjectionRefIn, double * padfGeoTransform, const char* pszOwner): responsiblePID(GDALGetResponsiblePIDForCurrentThread()), pszProjectionRef(pszProjectionRefIn ? CPLStrdup(pszProjectionRefIn): nullptr), bHasSrcProjection(pszProjectionRefIn != nullptr) { GDALDatasetPool::Ref(); SetDescription(pszSourceDatasetDescription); nRasterXSize = nRasterXSizeIn; nRasterYSize = nRasterYSizeIn; eAccess = eAccessIn; bShared = CPL_TO_BOOL(bSharedIn); m_pszOwner = pszOwner ? CPLStrdup(pszOwner) : nullptr; if (padfGeoTransform) { memcpy(adfGeoTransform, padfGeoTransform,6 * sizeof(double)); bHasSrcGeoTransform = true; } else { adfGeoTransform[0] = 0; adfGeoTransform[1] = 1; adfGeoTransform[2] = 0; adfGeoTransform[3] = 0; adfGeoTransform[4] = 0; adfGeoTransform[5] = 1; bHasSrcGeoTransform = false; } if( pszProjectionRefIn ) { m_poSRS = new OGRSpatialReference(); m_poSRS->importFromWkt(pszProjectionRefIn); m_bHasSrcSRS = true; } pszGCPProjection = nullptr; nGCPCount = 0; pasGCPList = nullptr; metadataSet = nullptr; metadataItemSet = nullptr; cacheEntry = nullptr; } /************************************************************************/ /* ~GDALProxyPoolDataset() */ /************************************************************************/ GDALProxyPoolDataset::~GDALProxyPoolDataset() { GDALDatasetPool::CloseDatasetIfZeroRefCount(GetDescription(), eAccess, m_pszOwner); /* See comment in constructor */ /* It is not really a genuine shared dataset, so we don't */ /* want ~GDALDataset() to try to release it from its */ /* shared dataset hashset. This will save a */ /* "Should not happen. Cannot find %s, this=%p in phSharedDatasetSet" debug message */ bShared = false; CPLFree(pszProjectionRef); CPLFree(pszGCPProjection); if (nGCPCount) { GDALDeinitGCPs( nGCPCount, pasGCPList ); CPLFree( pasGCPList ); } if (metadataSet) CPLHashSetDestroy(metadataSet); if (metadataItemSet) CPLHashSetDestroy(metadataItemSet); CPLFree(m_pszOwner); if( m_poSRS ) m_poSRS->Release(); if( m_poGCPSRS ) m_poGCPSRS->Release(); GDALDatasetPool::Unref(); } /************************************************************************/ /* SetOpenOptions() */ /************************************************************************/ void GDALProxyPoolDataset::SetOpenOptions(char** papszOpenOptionsIn) { CPLAssert(papszOpenOptions == nullptr); papszOpenOptions = CSLDuplicate(papszOpenOptionsIn); } /************************************************************************/ /* AddSrcBandDescription() */ /************************************************************************/ void GDALProxyPoolDataset::AddSrcBandDescription( GDALDataType eDataType, int nBlockXSize, int nBlockYSize) { SetBand(nBands + 1, new GDALProxyPoolRasterBand(this, nBands + 1, eDataType, nBlockXSize, nBlockYSize)); } /************************************************************************/ /* AddSrcBand() */ /************************************************************************/ void GDALProxyPoolDataset::AddSrcBand(int nBand, GDALDataType eDataType, int nBlockXSize, int nBlockYSize) { SetBand(nBand, new GDALProxyPoolRasterBand(this, nBand, eDataType, nBlockXSize, nBlockYSize)); } /************************************************************************/ /* RefUnderlyingDataset() */ /************************************************************************/ GDALDataset* GDALProxyPoolDataset::RefUnderlyingDataset() const { return RefUnderlyingDataset(true); } GDALDataset* GDALProxyPoolDataset::RefUnderlyingDataset(bool bForceOpen) const { /* We pretend that the current thread is responsiblePID, that is */ /* to say the thread that created that GDALProxyPoolDataset object. */ /* This is for the case when a GDALProxyPoolDataset is created by a */ /* thread and used by other threads. These other threads, when doing actual */ /* IO, will come there and potentially open the underlying dataset. */ /* By doing this, they can indirectly call GDALOpenShared() on .aux file */ /* for example. So this call to GDALOpenShared() must occur as if it */ /* was done by the creating thread, otherwise it will not be correctly closed afterwards... */ /* To make a long story short : this is necessary when warping with ChunkAndWarpMulti */ /* a VRT of GeoTIFFs that have associated .aux files */ GIntBig curResponsiblePID = GDALGetResponsiblePIDForCurrentThread(); GDALSetResponsiblePIDForCurrentThread(responsiblePID); cacheEntry = GDALDatasetPool::RefDataset(GetDescription(), eAccess, papszOpenOptions, GetShared(), bForceOpen, m_pszOwner); GDALSetResponsiblePIDForCurrentThread(curResponsiblePID); if (cacheEntry != nullptr) { if (cacheEntry->poDS != nullptr) return cacheEntry->poDS; else GDALDatasetPool::UnrefDataset(cacheEntry); } return nullptr; } /************************************************************************/ /* UnrefUnderlyingDataset() */ /************************************************************************/ void GDALProxyPoolDataset::UnrefUnderlyingDataset( CPL_UNUSED GDALDataset* poUnderlyingDataset ) const { if (cacheEntry != nullptr) { CPLAssert(cacheEntry->poDS == poUnderlyingDataset); if (cacheEntry->poDS != nullptr) GDALDatasetPool::UnrefDataset(cacheEntry); } } /************************************************************************/ /* FlushCache() */ /************************************************************************/ void GDALProxyPoolDataset::FlushCache() { GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(false); if (poUnderlyingDataset) { poUnderlyingDataset->FlushCache(); UnrefUnderlyingDataset(poUnderlyingDataset); } } /************************************************************************/ /* SetSpatialRef() */ /************************************************************************/ CPLErr GDALProxyPoolDataset::SetSpatialRef(const OGRSpatialReference* poSRS) { m_bHasSrcSRS = false; return GDALProxyDataset::SetSpatialRef(poSRS); } /************************************************************************/ /* GetSpatialRef() */ /************************************************************************/ const OGRSpatialReference* GDALProxyPoolDataset::GetSpatialRef() const { if (m_bHasSrcSRS) return m_poSRS; else { if( m_poSRS ) m_poSRS->Release(); m_poSRS = nullptr; auto poSRS = GDALProxyDataset::GetSpatialRef(); if( poSRS ) m_poSRS = poSRS->Clone(); return m_poSRS; } } /************************************************************************/ /* SetProjection() */ /************************************************************************/ CPLErr GDALProxyPoolDataset::_SetProjection(const char* pszProjectionRefIn) { bHasSrcProjection = false; return GDALProxyDataset::_SetProjection(pszProjectionRefIn); } /************************************************************************/ /* GetProjectionRef() */ /************************************************************************/ const char *GDALProxyPoolDataset::_GetProjectionRef() { if (bHasSrcProjection) return pszProjectionRef; else { CPLFree(pszProjectionRef); pszProjectionRef = CPLStrdup( GDALProxyDataset::_GetProjectionRef() ); return pszProjectionRef; } } /************************************************************************/ /* SetGeoTransform() */ /************************************************************************/ CPLErr GDALProxyPoolDataset::SetGeoTransform( double * padfGeoTransform ) { bHasSrcGeoTransform = false; return GDALProxyDataset::SetGeoTransform(padfGeoTransform); } /************************************************************************/ /* GetGeoTransform() */ /************************************************************************/ CPLErr GDALProxyPoolDataset::GetGeoTransform( double * padfGeoTransform ) { if (bHasSrcGeoTransform) { memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double)); return CE_None; } else { return GDALProxyDataset::GetGeoTransform(padfGeoTransform); } } /************************************************************************/ /* GetMetadata() */ /************************************************************************/ char **GDALProxyPoolDataset::GetMetadata( const char * pszDomain ) { if (metadataSet == nullptr) metadataSet = CPLHashSetNew(hash_func_get_metadata, equal_func_get_metadata, free_func_get_metadata); GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); if (poUnderlyingDataset == nullptr) return nullptr; char** papszUnderlyingMetadata = poUnderlyingDataset->GetMetadata(pszDomain); GetMetadataElt* pElt = static_cast(CPLMalloc(sizeof(GetMetadataElt))); pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : nullptr; pElt->papszMetadata = CSLDuplicate(papszUnderlyingMetadata); CPLHashSetInsert(metadataSet, pElt); UnrefUnderlyingDataset(poUnderlyingDataset); return pElt->papszMetadata; } /************************************************************************/ /* GetMetadataItem() */ /************************************************************************/ const char *GDALProxyPoolDataset::GetMetadataItem( const char * pszName, const char * pszDomain ) { if (metadataItemSet == nullptr) metadataItemSet = CPLHashSetNew(hash_func_get_metadata_item, equal_func_get_metadata_item, free_func_get_metadata_item); GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); if (poUnderlyingDataset == nullptr) return nullptr; const char* pszUnderlyingMetadataItem = poUnderlyingDataset->GetMetadataItem(pszName, pszDomain); GetMetadataItemElt* pElt = static_cast(CPLMalloc(sizeof(GetMetadataItemElt))); pElt->pszName = (pszName) ? CPLStrdup(pszName) : nullptr; pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : nullptr; pElt->pszMetadataItem = (pszUnderlyingMetadataItem) ? CPLStrdup(pszUnderlyingMetadataItem) : nullptr; CPLHashSetInsert(metadataItemSet, pElt); UnrefUnderlyingDataset(poUnderlyingDataset); return pElt->pszMetadataItem; } /************************************************************************/ /* GetInternalHandle() */ /************************************************************************/ void *GDALProxyPoolDataset::GetInternalHandle( const char * pszRequest) { CPLError(CE_Warning, CPLE_AppDefined, "GetInternalHandle() cannot be safely called on a proxy pool dataset\n" "as the returned value may be invalidated at any time.\n"); return GDALProxyDataset::GetInternalHandle(pszRequest); } /************************************************************************/ /* GetGCPSpatialRef() */ /************************************************************************/ const OGRSpatialReference *GDALProxyPoolDataset::GetGCPSpatialRef() const { GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); if (poUnderlyingDataset == nullptr) return nullptr; m_poGCPSRS->Release(); m_poGCPSRS = nullptr; const auto poUnderlyingGCPSRS = poUnderlyingDataset->GetGCPSpatialRef(); if (poUnderlyingGCPSRS) m_poGCPSRS = poUnderlyingGCPSRS->Clone(); UnrefUnderlyingDataset(poUnderlyingDataset); return m_poGCPSRS; } /************************************************************************/ /* GetGCPProjection() */ /************************************************************************/ const char *GDALProxyPoolDataset::_GetGCPProjection() { GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); if (poUnderlyingDataset == nullptr) return nullptr; CPLFree(pszGCPProjection); pszGCPProjection = nullptr; const char* pszUnderlyingGCPProjection = poUnderlyingDataset->_GetGCPProjection(); if (pszUnderlyingGCPProjection) pszGCPProjection = CPLStrdup(pszUnderlyingGCPProjection); UnrefUnderlyingDataset(poUnderlyingDataset); return pszGCPProjection; } /************************************************************************/ /* GetGCPs() */ /************************************************************************/ const GDAL_GCP *GDALProxyPoolDataset::GetGCPs() { GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); if (poUnderlyingDataset == nullptr) return nullptr; if (nGCPCount) { GDALDeinitGCPs( nGCPCount, pasGCPList ); CPLFree( pasGCPList ); pasGCPList = nullptr; } const GDAL_GCP* pasUnderlyingGCPList = poUnderlyingDataset->GetGCPs(); nGCPCount = poUnderlyingDataset->GetGCPCount(); if (nGCPCount) pasGCPList = GDALDuplicateGCPs(nGCPCount, pasUnderlyingGCPList ); UnrefUnderlyingDataset(poUnderlyingDataset); return pasGCPList; } /************************************************************************/ /* GDALProxyPoolDatasetCreate() */ /************************************************************************/ GDALProxyPoolDatasetH GDALProxyPoolDatasetCreate(const char* pszSourceDatasetDescription, int nRasterXSize, int nRasterYSize, GDALAccess eAccess, int bShared, const char * pszProjectionRef, double * padfGeoTransform) { return reinterpret_cast( new GDALProxyPoolDataset(pszSourceDatasetDescription, nRasterXSize, nRasterYSize, eAccess, bShared, pszProjectionRef, padfGeoTransform)); } /************************************************************************/ /* GDALProxyPoolDatasetDelete() */ /************************************************************************/ void CPL_DLL GDALProxyPoolDatasetDelete(GDALProxyPoolDatasetH hProxyPoolDataset) { delete reinterpret_cast(hProxyPoolDataset); } /************************************************************************/ /* GDALProxyPoolDatasetAddSrcBandDescription() */ /************************************************************************/ void GDALProxyPoolDatasetAddSrcBandDescription( GDALProxyPoolDatasetH hProxyPoolDataset, GDALDataType eDataType, int nBlockXSize, int nBlockYSize) { reinterpret_cast(hProxyPoolDataset)-> AddSrcBandDescription(eDataType, nBlockXSize, nBlockYSize); } /* ******************************************************************** */ /* GDALProxyPoolRasterBand() */ /* ******************************************************************** */ GDALProxyPoolRasterBand::GDALProxyPoolRasterBand(GDALProxyPoolDataset* poDSIn, int nBandIn, GDALDataType eDataTypeIn, int nBlockXSizeIn, int nBlockYSizeIn) { poDS = poDSIn; nBand = nBandIn; eDataType = eDataTypeIn; nRasterXSize = poDSIn->GetRasterXSize(); nRasterYSize = poDSIn->GetRasterYSize(); nBlockXSize = nBlockXSizeIn; nBlockYSize = nBlockYSizeIn; } /* ******************************************************************** */ /* GDALProxyPoolRasterBand() */ /* ******************************************************************** */ GDALProxyPoolRasterBand::GDALProxyPoolRasterBand(GDALProxyPoolDataset* poDSIn, GDALRasterBand* poUnderlyingRasterBand) { poDS = poDSIn; nBand = poUnderlyingRasterBand->GetBand(); eDataType = poUnderlyingRasterBand->GetRasterDataType(); nRasterXSize = poUnderlyingRasterBand->GetXSize(); nRasterYSize = poUnderlyingRasterBand->GetYSize(); poUnderlyingRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize); } /* ******************************************************************** */ /* ~GDALProxyPoolRasterBand() */ /* ******************************************************************** */ GDALProxyPoolRasterBand::~GDALProxyPoolRasterBand() { if (metadataSet) CPLHashSetDestroy(metadataSet); if (metadataItemSet) CPLHashSetDestroy(metadataItemSet); CPLFree(pszUnitType); CSLDestroy(papszCategoryNames); if (poColorTable) delete poColorTable; for( int i=0; i < nSizeProxyOverviewRasterBand; i++ ) { if (papoProxyOverviewRasterBand[i]) delete papoProxyOverviewRasterBand[i]; } CPLFree(papoProxyOverviewRasterBand); if (poProxyMaskBand) delete poProxyMaskBand; } /************************************************************************/ /* AddSrcMaskBandDescription() */ /************************************************************************/ void GDALProxyPoolRasterBand::AddSrcMaskBandDescription( GDALDataType eDataTypeIn, int nBlockXSizeIn, int nBlockYSizeIn) { CPLAssert(poProxyMaskBand == nullptr); poProxyMaskBand = new GDALProxyPoolMaskBand(cpl::down_cast(poDS), this, eDataTypeIn, nBlockXSizeIn, nBlockYSizeIn); } /************************************************************************/ /* RefUnderlyingRasterBand() */ /************************************************************************/ GDALRasterBand* GDALProxyPoolRasterBand::RefUnderlyingRasterBand(bool bForceOpen) { GDALDataset* poUnderlyingDataset = (cpl::down_cast(poDS))-> RefUnderlyingDataset(bForceOpen); if (poUnderlyingDataset == nullptr) return nullptr; GDALRasterBand* poBand = poUnderlyingDataset->GetRasterBand(nBand); if (poBand == nullptr) { (cpl::down_cast(poDS))->UnrefUnderlyingDataset(poUnderlyingDataset); } else if(nBlockXSize <= 0 || nBlockYSize <= 0) { // Here we try to load nBlockXSize&nBlockYSize from underlying band // but we must guarantee that we will not access directly to // nBlockXSize/nBlockYSize before RefUnderlyingRasterBand() is called int nSrcBlockXSize, nSrcBlockYSize; poBand->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize); nBlockXSize = nSrcBlockXSize; nBlockYSize = nSrcBlockYSize; } return poBand; } GDALRasterBand* GDALProxyPoolRasterBand::RefUnderlyingRasterBand() { return RefUnderlyingRasterBand(true); } /************************************************************************/ /* UnrefUnderlyingRasterBand() */ /************************************************************************/ void GDALProxyPoolRasterBand::UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand) { if (poUnderlyingRasterBand) (cpl::down_cast(poDS))-> UnrefUnderlyingDataset(poUnderlyingRasterBand->GetDataset()); } /************************************************************************/ /* FlushCache() */ /************************************************************************/ CPLErr GDALProxyPoolRasterBand::FlushCache() { GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(false); if (poUnderlyingRasterBand) { CPLErr eErr = poUnderlyingRasterBand->FlushCache(); UnrefUnderlyingRasterBand(poUnderlyingRasterBand); return eErr; } return CE_None; } /************************************************************************/ /* GetMetadata() */ /************************************************************************/ char **GDALProxyPoolRasterBand::GetMetadata( const char * pszDomain ) { if (metadataSet == nullptr) metadataSet = CPLHashSetNew(hash_func_get_metadata, equal_func_get_metadata, free_func_get_metadata); GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); if (poUnderlyingRasterBand == nullptr) return nullptr; char** papszUnderlyingMetadata = poUnderlyingRasterBand->GetMetadata(pszDomain); GetMetadataElt* pElt = static_cast(CPLMalloc(sizeof(GetMetadataElt))); pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : nullptr; pElt->papszMetadata = CSLDuplicate(papszUnderlyingMetadata); CPLHashSetInsert(metadataSet, pElt); UnrefUnderlyingRasterBand(poUnderlyingRasterBand); return pElt->papszMetadata; } /************************************************************************/ /* GetMetadataItem() */ /************************************************************************/ const char *GDALProxyPoolRasterBand::GetMetadataItem( const char * pszName, const char * pszDomain ) { if (metadataItemSet == nullptr) metadataItemSet = CPLHashSetNew(hash_func_get_metadata_item, equal_func_get_metadata_item, free_func_get_metadata_item); GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); if (poUnderlyingRasterBand == nullptr) return nullptr; const char* pszUnderlyingMetadataItem = poUnderlyingRasterBand->GetMetadataItem(pszName, pszDomain); GetMetadataItemElt* pElt = static_cast(CPLMalloc(sizeof(GetMetadataItemElt))); pElt->pszName = (pszName) ? CPLStrdup(pszName) : nullptr; pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : nullptr; pElt->pszMetadataItem = (pszUnderlyingMetadataItem) ? CPLStrdup(pszUnderlyingMetadataItem) : nullptr; CPLHashSetInsert(metadataItemSet, pElt); UnrefUnderlyingRasterBand(poUnderlyingRasterBand); return pElt->pszMetadataItem; } /* ******************************************************************** */ /* GetCategoryNames() */ /* ******************************************************************** */ char **GDALProxyPoolRasterBand::GetCategoryNames() { GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); if (poUnderlyingRasterBand == nullptr) return nullptr; CSLDestroy(papszCategoryNames); papszCategoryNames = nullptr; char** papszUnderlyingCategoryNames = poUnderlyingRasterBand->GetCategoryNames(); if (papszUnderlyingCategoryNames) papszCategoryNames = CSLDuplicate(papszUnderlyingCategoryNames); UnrefUnderlyingRasterBand(poUnderlyingRasterBand); return papszCategoryNames; } /* ******************************************************************** */ /* GetUnitType() */ /* ******************************************************************** */ const char *GDALProxyPoolRasterBand::GetUnitType() { GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); if (poUnderlyingRasterBand == nullptr) return nullptr; CPLFree(pszUnitType); pszUnitType = nullptr; const char* pszUnderlyingUnitType = poUnderlyingRasterBand->GetUnitType(); if (pszUnderlyingUnitType) pszUnitType = CPLStrdup(pszUnderlyingUnitType); UnrefUnderlyingRasterBand(poUnderlyingRasterBand); return pszUnitType; } /* ******************************************************************** */ /* GetColorTable() */ /* ******************************************************************** */ GDALColorTable *GDALProxyPoolRasterBand::GetColorTable() { GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); if (poUnderlyingRasterBand == nullptr) return nullptr; if (poColorTable) delete poColorTable; poColorTable = nullptr; GDALColorTable* poUnderlyingColorTable = poUnderlyingRasterBand->GetColorTable(); if (poUnderlyingColorTable) poColorTable = poUnderlyingColorTable->Clone(); UnrefUnderlyingRasterBand(poUnderlyingRasterBand); return poColorTable; } /* ******************************************************************** */ /* GetOverview() */ /* ******************************************************************** */ GDALRasterBand *GDALProxyPoolRasterBand::GetOverview(int nOverviewBand) { if (nOverviewBand >= 0 && nOverviewBand < nSizeProxyOverviewRasterBand) { if (papoProxyOverviewRasterBand[nOverviewBand]) return papoProxyOverviewRasterBand[nOverviewBand]; } GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); if (poUnderlyingRasterBand == nullptr) return nullptr; GDALRasterBand* poOverviewRasterBand = poUnderlyingRasterBand->GetOverview(nOverviewBand); if (poOverviewRasterBand == nullptr) { UnrefUnderlyingRasterBand(poUnderlyingRasterBand); return nullptr; } if (nOverviewBand >= nSizeProxyOverviewRasterBand) { papoProxyOverviewRasterBand = static_cast( CPLRealloc(papoProxyOverviewRasterBand, sizeof(GDALProxyPoolOverviewRasterBand*) * (nOverviewBand + 1))); for( int i=nSizeProxyOverviewRasterBand; i(poDS), poOverviewRasterBand, this, nOverviewBand); UnrefUnderlyingRasterBand(poUnderlyingRasterBand); return papoProxyOverviewRasterBand[nOverviewBand]; } /* ******************************************************************** */ /* GetRasterSampleOverview() */ /* ******************************************************************** */ GDALRasterBand *GDALProxyPoolRasterBand::GetRasterSampleOverview( GUIntBig /* nDesiredSamples */ ) { CPLError(CE_Failure, CPLE_AppDefined, "GDALProxyPoolRasterBand::GetRasterSampleOverview : not implemented yet"); return nullptr; } /* ******************************************************************** */ /* GetMaskBand() */ /* ******************************************************************** */ GDALRasterBand *GDALProxyPoolRasterBand::GetMaskBand() { if (poProxyMaskBand) return poProxyMaskBand; GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); if (poUnderlyingRasterBand == nullptr) return nullptr; GDALRasterBand* poMaskBand = poUnderlyingRasterBand->GetMaskBand(); poProxyMaskBand = new GDALProxyPoolMaskBand( cpl::down_cast(poDS), poMaskBand, this); UnrefUnderlyingRasterBand(poUnderlyingRasterBand); return poProxyMaskBand; } /* ******************************************************************** */ /* GDALProxyPoolOverviewRasterBand() */ /* ******************************************************************** */ GDALProxyPoolOverviewRasterBand::GDALProxyPoolOverviewRasterBand(GDALProxyPoolDataset* poDSIn, GDALRasterBand* poUnderlyingOverviewBand, GDALProxyPoolRasterBand* poMainBandIn, int nOverviewBandIn) : GDALProxyPoolRasterBand(poDSIn, poUnderlyingOverviewBand), poMainBand(poMainBandIn), nOverviewBand(nOverviewBandIn) { } /* ******************************************************************** */ /* ~GDALProxyPoolOverviewRasterBand() */ /* ******************************************************************** */ GDALProxyPoolOverviewRasterBand::~GDALProxyPoolOverviewRasterBand() { CPLAssert(nRefCountUnderlyingMainRasterBand == 0); } /* ******************************************************************** */ /* RefUnderlyingRasterBand() */ /* ******************************************************************** */ GDALRasterBand* GDALProxyPoolOverviewRasterBand::RefUnderlyingRasterBand() { poUnderlyingMainRasterBand = poMainBand->RefUnderlyingRasterBand(); if (poUnderlyingMainRasterBand == nullptr) return nullptr; nRefCountUnderlyingMainRasterBand ++; return poUnderlyingMainRasterBand->GetOverview(nOverviewBand); } /* ******************************************************************** */ /* UnrefUnderlyingRasterBand() */ /* ******************************************************************** */ void GDALProxyPoolOverviewRasterBand::UnrefUnderlyingRasterBand( GDALRasterBand* /* poUnderlyingRasterBand */ ) { poMainBand->UnrefUnderlyingRasterBand(poUnderlyingMainRasterBand); nRefCountUnderlyingMainRasterBand --; } /* ******************************************************************** */ /* GDALProxyPoolMaskBand() */ /* ******************************************************************** */ GDALProxyPoolMaskBand::GDALProxyPoolMaskBand(GDALProxyPoolDataset* poDSIn, GDALRasterBand* poUnderlyingMaskBand, GDALProxyPoolRasterBand* poMainBandIn) : GDALProxyPoolRasterBand(poDSIn, poUnderlyingMaskBand) { poMainBand = poMainBandIn; poUnderlyingMainRasterBand = nullptr; nRefCountUnderlyingMainRasterBand = 0; } /* ******************************************************************** */ /* GDALProxyPoolMaskBand() */ /* ******************************************************************** */ GDALProxyPoolMaskBand::GDALProxyPoolMaskBand(GDALProxyPoolDataset* poDSIn, GDALProxyPoolRasterBand* poMainBandIn, GDALDataType eDataTypeIn, int nBlockXSizeIn, int nBlockYSizeIn) : GDALProxyPoolRasterBand(poDSIn, 1, eDataTypeIn, nBlockXSizeIn, nBlockYSizeIn), poMainBand(poMainBandIn) { } /* ******************************************************************** */ /* ~GDALProxyPoolMaskBand() */ /* ******************************************************************** */ GDALProxyPoolMaskBand::~GDALProxyPoolMaskBand() { CPLAssert(nRefCountUnderlyingMainRasterBand == 0); } /* ******************************************************************** */ /* RefUnderlyingRasterBand() */ /* ******************************************************************** */ GDALRasterBand* GDALProxyPoolMaskBand::RefUnderlyingRasterBand() { poUnderlyingMainRasterBand = poMainBand->RefUnderlyingRasterBand(); if (poUnderlyingMainRasterBand == nullptr) return nullptr; nRefCountUnderlyingMainRasterBand ++; return poUnderlyingMainRasterBand->GetMaskBand(); } /* ******************************************************************** */ /* UnrefUnderlyingRasterBand() */ /* ******************************************************************** */ void GDALProxyPoolMaskBand::UnrefUnderlyingRasterBand( GDALRasterBand* /* poUnderlyingRasterBand */ ) { poMainBand->UnrefUnderlyingRasterBand(poUnderlyingMainRasterBand); nRefCountUnderlyingMainRasterBand --; } //! @endcond