Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport release-3_40] Fix STAC memory leaks & Port more API to use unique_ptr #60720

Merged
merged 2 commits into from
Feb 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 79 additions & 54 deletions src/core/stac/qgsstaccontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "moc_qgsstaccontroller.cpp"
#include "qgsstaccatalog.h"
#include "qgsstaccollection.h"
#include "qgsstaccollections.h"
#include "qgsstacitem.h"
#include "qgsstacitemcollection.h"
#include "qgsstacparser.h"
Expand All @@ -32,9 +33,10 @@
QgsStacController::~QgsStacController()
{
qDeleteAll( mReplies );
qDeleteAll( mFetchedStacObjects );
qDeleteAll( mFetchedItemCollections );
}


int QgsStacController::fetchStacObjectAsync( const QUrl &url )
{
QNetworkReply *reply = fetchAsync( url );
Expand Down Expand Up @@ -102,7 +104,7 @@ void QgsStacController::handleStacObjectReply()
parser.setData( data );
parser.setBaseUrl( reply->url() );

QgsStacObject *object = nullptr;
std::unique_ptr< QgsStacObject > object;
switch ( parser.type() )
{
case QgsStacObject::Type::Catalog:
Expand All @@ -118,7 +120,7 @@ void QgsStacController::handleStacObjectReply()
object = nullptr;
break;
}
mFetchedStacObjects.insert( requestId, object );
mFetchedStacObjects.insert( requestId, object.release() );
emit finishedStacObjectRequest( requestId, parser.error() );
reply->deleteLater();
mReplies.removeOne( reply );
Expand Down Expand Up @@ -146,64 +148,37 @@ void QgsStacController::handleItemCollectionReply()
parser.setData( data );
parser.setBaseUrl( reply->url() );

QgsStacItemCollection *fc = parser.itemCollection();
mFetchedItemCollections.insert( requestId, fc );
std::unique_ptr<QgsStacItemCollection> fc = parser.itemCollection();
mFetchedItemCollections.insert( requestId, fc.release() );
emit finishedItemCollectionRequest( requestId, parser.error() );
reply->deleteLater();
mReplies.removeOne( reply );
}

QgsStacObject *QgsStacController::takeStacObject( int requestId )
{
return mFetchedStacObjects.take( requestId );
}

QgsStacItemCollection *QgsStacController::takeItemCollection( int requestId )
{
return mFetchedItemCollections.take( requestId );
}

QgsStacObject *QgsStacController::fetchStacObject( const QUrl &url, QString *error )
template<class T>
std::unique_ptr<T> QgsStacController::takeStacObject( int requestId )
{
QgsNetworkReplyContent content = fetchBlocking( url );
std::unique_ptr< QgsStacObject > obj( mFetchedStacObjects.take( requestId ) );

if ( content.error() != QNetworkReply::NoError )
{
if ( error )
*error = content.errorString();

return nullptr;
}

const QByteArray data = content.content();

QgsStacParser parser;
parser.setData( data );
parser.setBaseUrl( url );
QgsStacObject *object = nullptr;
switch ( parser.type() )
if ( T *downCastObj = dynamic_cast< T * >( obj.get() ) )
{
case QgsStacObject::Type::Catalog:
object = parser.catalog();
break;
case QgsStacObject::Type::Collection:
object = parser.collection();
break;
case QgsStacObject::Type::Item:
object = parser.item();
break;
case QgsStacObject::Type::Unknown:
object = nullptr;
break;
( void )obj.release();
return std::unique_ptr< T >( downCastObj );
}

if ( error )
*error = parser.error();
return nullptr;
}
template CORE_EXPORT std::unique_ptr< QgsStacItem > QgsStacController::takeStacObject<QgsStacItem>( int requestId );
template CORE_EXPORT std::unique_ptr< QgsStacCatalog > QgsStacController::takeStacObject<QgsStacCatalog>( int requestId );
template CORE_EXPORT std::unique_ptr< QgsStacObject > QgsStacController::takeStacObject<QgsStacObject>( int requestId );

return object;
std::unique_ptr< QgsStacItemCollection > QgsStacController::takeItemCollection( int requestId )
{
std::unique_ptr< QgsStacItemCollection > col( mFetchedItemCollections.take( requestId ) );
return col;
}

QgsStacItemCollection *QgsStacController::fetchItemCollection( const QUrl &url, QString *error )
std::unique_ptr< QgsStacItemCollection > QgsStacController::fetchItemCollection( const QUrl &url, QString *error )
{
QgsNetworkReplyContent content = fetchBlocking( url );

Expand All @@ -220,15 +195,15 @@ QgsStacItemCollection *QgsStacController::fetchItemCollection( const QUrl &url,
QgsStacParser parser;
parser.setData( data );
parser.setBaseUrl( url );
QgsStacItemCollection *ic = parser.itemCollection();
std::unique_ptr< QgsStacItemCollection > ic( parser.itemCollection() );

if ( error )
*error = parser.error();

return ic;
}

QgsStacCollections *QgsStacController::fetchCollections( const QUrl &url, QString *error )
std::unique_ptr< QgsStacCollections > QgsStacController::fetchCollections( const QUrl &url, QString *error )
{
QgsNetworkReplyContent content = fetchBlocking( url );

Expand All @@ -244,7 +219,7 @@ QgsStacCollections *QgsStacController::fetchCollections( const QUrl &url, QStrin

QgsStacParser parser;
parser.setData( data );
QgsStacCollections *col = parser.collections();
std::unique_ptr< QgsStacCollections > col( parser.collections() );

if ( error )
*error = parser.error();
Expand Down Expand Up @@ -280,7 +255,7 @@ void QgsStacController::setAuthCfg( const QString &authCfg )
mAuthCfg = authCfg;
}

QgsStacCatalog *QgsStacController::openLocalCatalog( const QString &fileName ) const
std::unique_ptr<QgsStacCatalog> QgsStacController::openLocalCatalog( const QString &fileName ) const
{
QFile file( fileName );
const bool ok = file.open( QIODevice::ReadOnly );
Expand All @@ -297,7 +272,7 @@ QgsStacCatalog *QgsStacController::openLocalCatalog( const QString &fileName ) c
}


QgsStacCollection *QgsStacController::openLocalCollection( const QString &fileName ) const
std::unique_ptr<QgsStacCollection> QgsStacController::openLocalCollection( const QString &fileName ) const
{
QFile file( fileName );
const bool ok = file.open( QIODevice::ReadOnly );
Expand All @@ -313,7 +288,7 @@ QgsStacCollection *QgsStacController::openLocalCollection( const QString &fileNa
return parser.collection();
}

QgsStacItem *QgsStacController::openLocalItem( const QString &fileName ) const
std::unique_ptr<QgsStacItem> QgsStacController::openLocalItem( const QString &fileName ) const
{
QFile file( fileName );
const bool ok = file.open( QIODevice::ReadOnly );
Expand All @@ -329,7 +304,57 @@ QgsStacItem *QgsStacController::openLocalItem( const QString &fileName ) const
return parser.item();
}

template<class T>
std::unique_ptr<T> QgsStacController::fetchStacObject( const QUrl &url, QString *error )
{
QgsNetworkReplyContent content = fetchBlocking( url );

if ( content.error() != QNetworkReply::NoError )
{
if ( error )
*error = content.errorString();

return nullptr;
}

const QByteArray data = content.content();

QgsStacParser parser;
parser.setData( data );
parser.setBaseUrl( url );
std::unique_ptr< QgsStacObject > object;
switch ( parser.type() )
{
case QgsStacObject::Type::Catalog:
object = parser.catalog();
break;
case QgsStacObject::Type::Collection:
object = parser.collection();
break;
case QgsStacObject::Type::Item:
object = parser.item();
break;
case QgsStacObject::Type::Unknown:
break;
}

std::unique_ptr< T > res;
if ( T *castObject = dynamic_cast< T * >( object.get() ) )
{
( void )object.release();
res.reset( castObject );
}
else
{
QgsDebugError( "Retrieved STAC object could not be cast to expected type" );
}

if ( error )
*error = parser.error();

return res;
}

template CORE_EXPORT std::unique_ptr< QgsStacItem > QgsStacController::fetchStacObject<QgsStacItem>( const QUrl &url, QString *error );
template CORE_EXPORT std::unique_ptr< QgsStacCollection > QgsStacController::fetchStacObject<QgsStacCollection>( const QUrl &url, QString *error );
template CORE_EXPORT std::unique_ptr< QgsStacCatalog > QgsStacController::fetchStacObject<QgsStacCatalog>( const QUrl &url, QString *error );
17 changes: 9 additions & 8 deletions src/core/stac/qgsstaccontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "qgis_core.h"
#include "qgshttpheaders.h"
#include "qgsnetworkreply.h"
#include "qgsstacobject.h"

class QgsStacObject;
class QgsStacCatalog;
Expand Down Expand Up @@ -56,40 +57,40 @@ class CORE_EXPORT QgsStacController : public QObject
* Returns a STAC Catalog by parsing a local file
* The caller takes ownership of the returned catalog
*/
QgsStacCatalog *openLocalCatalog( const QString &fileName ) const;
std::unique_ptr< QgsStacCatalog > openLocalCatalog( const QString &fileName ) const;

/**
* Returns a STAC Collection by parsing a local file
* The caller takes ownership of the returned collection
*/
QgsStacCollection *openLocalCollection( const QString &fileName ) const;
std::unique_ptr< QgsStacCollection > openLocalCollection( const QString &fileName ) const;

/**
* Returns a STAC Item by parsing a local file
* The caller takes ownership of the returned item
*/
QgsStacItem *openLocalItem( const QString &fileName ) const;
std::unique_ptr< QgsStacItem > openLocalItem( const QString &fileName ) const;

/**
* Fetches a STAC object from \a url using a blocking network request.
* An optional \a error parameter will be populated with any network error information.
* The caller takes ownership of the returned object
*/
QgsStacObject *fetchStacObject( const QUrl &url, QString *error = nullptr );
template<class T> std::unique_ptr< T > fetchStacObject( const QUrl &url, QString *error = nullptr );

/**
* Fetches a feature collection from \a url using a blocking network request.
* An optional \a error parameter will be populated with any network error information.
* The caller takes ownership of the returned feature collection
*/
QgsStacItemCollection *fetchItemCollection( const QUrl &url, QString *error = nullptr );
std::unique_ptr< QgsStacItemCollection > fetchItemCollection( const QUrl &url, QString *error = nullptr );

/**
* Fetches collections from \a url using a blocking network request.
* An optional \a error parameter will be populated with any network error information.
* The caller takes ownership of the returned feature collection
*/
QgsStacCollections *fetchCollections( const QUrl &url, QString *error = nullptr );
std::unique_ptr< QgsStacCollections > fetchCollections( const QUrl &url, QString *error = nullptr );

/**
* Initiates an asynchronous request for a STAC object using the \a url
Expand All @@ -115,7 +116,7 @@ class CORE_EXPORT QgsStacController : public QObject
* \see fetchStacObjectAsync
* \see finishedStacObjectRequest
*/
QgsStacObject *takeStacObject( int requestId );
template<class T> std::unique_ptr< T > takeStacObject( int requestId );

/**
* Returns the feature collection fetched with the specified \a requestId
Expand All @@ -125,7 +126,7 @@ class CORE_EXPORT QgsStacController : public QObject
* \see fetchItemCollectionAsync
* \see finishedItemCollectionRequest
*/
QgsStacItemCollection *takeItemCollection( int requestId );
std::unique_ptr< QgsStacItemCollection > takeItemCollection( int requestId );

/**
* Returns the authentication config id which will be used during the request.
Expand Down
Loading
Loading