Skip to content

Commit

Permalink
Add QgsFeature::approximateMemoryUsage()
Browse files Browse the repository at this point in the history
  • Loading branch information
rouault authored and nyalldawson committed Oct 18, 2020
1 parent 7ac4b51 commit a04fcce
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 0 deletions.
11 changes: 11 additions & 0 deletions python/core/auto_generated/qgsfeature.sip.in
Expand Up @@ -547,6 +547,17 @@ before this method can be used.
:return: -1 if field does not exist or field map is not associated.

.. seealso:: :py:func:`setFields`
%End

int approximateMemoryUsage() const;
%Docstring
Returns the approximate RAM usage of the feature, in bytes.

This method takes into account the size of variable elements (strings,
geometry, ...), but the value returned should be considered as a lower
bound estimation.

.. versionadded:: 3.16
%End

operator QVariant() const;
Expand Down
54 changes: 54 additions & 0 deletions src/core/qgsfeature.cpp
Expand Up @@ -18,6 +18,8 @@ email : sherman at mrcc.com
#include "qgsfields.h"
#include "qgsgeometry.h"
#include "qgsrectangle.h"
#include "qgsfield_p.h" // for approximateMemoryUsage()
#include "qgsfields_p.h" // for approximateMemoryUsage()

#include "qgsmessagelog.h"

Expand Down Expand Up @@ -279,6 +281,58 @@ int QgsFeature::fieldNameIndex( const QString &fieldName ) const
return d->fields.lookupField( fieldName );
}

static size_t qgsQStringApproximateMemoryUsage( const QString &str )
{
return sizeof( QString ) + str.size() * sizeof( QChar );
}

static size_t qgsQVariantApproximateMemoryUsage( const QVariant &v )
{
// A QVariant has a private structure that is a union of things whose larger
// size if a long long, and a int
size_t s = sizeof( QVariant ) + sizeof( long long ) + sizeof( int );
if ( v.type() == QVariant::String )
{
s += qgsQStringApproximateMemoryUsage( v.toString() );
}
else if ( v.type() == QVariant::StringList )
{
for ( const QString &str : v.toStringList() )
s += qgsQStringApproximateMemoryUsage( str );
}
else if ( v.type() == QVariant::List )
{
for ( const QVariant &subV : v.toList() )
s += qgsQVariantApproximateMemoryUsage( subV );
}
return s;
}

int QgsFeature::approximateMemoryUsage() const
{
size_t s = sizeof( *this ) + sizeof( *d );

// Attributes
for ( const QVariant &attr : qgis::as_const( d->attributes ) )
{
s += qgsQVariantApproximateMemoryUsage( attr );
}

// Geometry
s += sizeof( QAtomicInt ) + sizeof( void * ); // ~ sizeof(QgsGeometryPrivate)
// For simplicity we consider that the RAM usage is the one of the WKB
// representation
s += d->geometry.wkbSize();

// Fields
s += sizeof( QgsFieldsPrivate );
// TODO potentially: take into account the length of the name, comment, default value, etc...
s += d->fields.size() * ( sizeof( QgsField ) + sizeof( QgsFieldPrivate ) );

return static_cast<int>( s );
}


/***************************************************************************
* This class is considered CRITICAL and any change MUST be accompanied with
* full unit tests in testqgsfeature.cpp.
Expand Down
11 changes: 11 additions & 0 deletions src/core/qgsfeature.h
Expand Up @@ -536,6 +536,17 @@ class CORE_EXPORT QgsFeature
*/
int fieldNameIndex( const QString &fieldName ) const;

/**
* Returns the approximate RAM usage of the feature, in bytes.
*
* This method takes into account the size of variable elements (strings,
* geometry, ...), but the value returned should be considered as a lower
* bound estimation.
*
* \since QGIS 3.16
*/
int approximateMemoryUsage() const;

//! Allows direct construction of QVariants from features.
operator QVariant() const
{
Expand Down
5 changes: 5 additions & 0 deletions tests/src/core/testqgsfeature.cpp
Expand Up @@ -124,6 +124,7 @@ void TestQgsFeature::attributesTest()
void TestQgsFeature::constructorTest()
{
QgsFeature f;
QVERIFY( f.approximateMemoryUsage() > 0 );
QVERIFY( FID_IS_NULL( f.id() ) );
QgsFeature f2 { QgsFields() };
QVERIFY( FID_IS_NULL( f2.id() ) );
Expand Down Expand Up @@ -222,6 +223,7 @@ void TestQgsFeature::attributes()
feature.setAttributes( mAttrs );
QCOMPARE( feature.attributes(), mAttrs );
QCOMPARE( feature.attributes(), mAttrs );
QVERIFY( feature.approximateMemoryUsage() > QgsFeature().approximateMemoryUsage() );

//test implicit sharing detachment
QgsFeature copy( feature );
Expand Down Expand Up @@ -286,6 +288,7 @@ void TestQgsFeature::geometry()
//test no double delete of geometry when setting:
feature.setGeometry( QgsGeometry( mGeometry2 ) );
QVERIFY( feature.hasGeometry() );
QVERIFY( feature.approximateMemoryUsage() > QgsFeature().approximateMemoryUsage() );
feature.setGeometry( QgsGeometry( mGeometry ) );
QCOMPARE( feature.geometry().asWkb(), mGeometry.asWkb() );

Expand Down Expand Up @@ -355,6 +358,8 @@ void TestQgsFeature::fields()
QVERIFY( original.fields().isEmpty() );
original.setFields( mFields );
QCOMPARE( original.fields(), mFields );
QVERIFY( original.approximateMemoryUsage() > QgsFeature().approximateMemoryUsage() );

QgsFeature copy( original );
QCOMPARE( copy.fields(), original.fields() );

Expand Down

0 comments on commit a04fcce

Please sign in to comment.