diff --git a/src/modules/partition/core/PartUtils.cpp b/src/modules/partition/core/PartUtils.cpp index 04e857774..b923ddbbb 100644 --- a/src/modules/partition/core/PartUtils.cpp +++ b/src/modules/partition/core/PartUtils.cpp @@ -43,6 +43,216 @@ namespace PartUtils { +static const NamedEnumTable& +unitSuffixes() +{ + static const NamedEnumTable names{ + { QStringLiteral( "%" ), SizeUnit::Percent }, + { QStringLiteral( "B" ), SizeUnit::Byte }, + { QStringLiteral( "K" ), SizeUnit::KiB }, + { QStringLiteral( "M" ), SizeUnit::MiB }, + { QStringLiteral( "G" ), SizeUnit::GiB } + }; + + return names; +} + +PartSize::PartSize( const QString& s ) + : NamedSuffix( unitSuffixes(), s ) +{ + if ( ( unit() == SizeUnit::Percent ) && ( value() > 100 || value() < 0 ) ) + { + cDebug() << "Percent value" << value() << "is not valid."; + m_value = 0; + } + + if ( m_unit == SizeUnit::None ) + { + m_value = s.toInt(); + if ( m_value > 0 ) + m_unit = SizeUnit::Byte; + } + + if ( m_value <= 0 ) + { + m_value = 0; + m_unit = SizeUnit::None; + } +} + +qint64 +PartSize::toSectors( qint64 totalSectors, qint64 sectorSize ) const +{ + if ( !isValid() ) + return -1; + if ( totalSectors < 1 || sectorSize < 1 ) + return -1; + + switch ( m_unit ) + { + case unit_t::None: + return -1; + case unit_t::Percent: + if ( value() == 100 ) + return totalSectors; // Common-case, avoid futzing around + else + return totalSectors * value() / 100; + case unit_t::Byte: + case unit_t::KiB: + case unit_t::MiB: + case unit_t::GiB: + return bytesToSectors ( toBytes(), sectorSize ); + } + + return -1; +} + +qint64 +PartSize::toBytes( qint64 totalSectors, qint64 sectorSize ) const +{ + if ( !isValid() ) + return -1; + + switch ( m_unit ) + { + case unit_t::None: + return -1; + case unit_t::Percent: + if ( totalSectors < 1 || sectorSize < 1 ) + return -1; + if ( value() == 100 ) + return totalSectors * sectorSize; // Common-case, avoid futzing around + else + return totalSectors * value() / 100; + case unit_t::Byte: + case unit_t::KiB: + case unit_t::MiB: + case unit_t::GiB: + return toBytes(); + } + + // notreached + return -1; +} + +qint64 +PartSize::toBytes( qint64 totalBytes ) const +{ + if ( !isValid() ) + return -1; + + switch ( m_unit ) + { + case unit_t::None: + return -1; + case unit_t::Percent: + if ( totalBytes < 1 ) + return -1; + if ( value() == 100 ) + return totalBytes; // Common-case, avoid futzing around + else + return totalBytes * value() / 100; + case unit_t::Byte: + case unit_t::KiB: + case unit_t::MiB: + case unit_t::GiB: + return toBytes(); + } + + // notreached + return -1; +} + +qint64 +PartSize::toBytes() const +{ + if ( !isValid() ) + return -1; + + switch ( m_unit ) + { + case unit_t::Byte: + return value(); + case unit_t::KiB: + return CalamaresUtils::KiBtoBytes( static_cast( value() ) ); + case unit_t::MiB: + return CalamaresUtils::MiBtoBytes( static_cast( value() ) ); + case unit_t::GiB: + return CalamaresUtils::GiBtoBytes( static_cast( value() ) ); + default: + break; + } + + // Reached only when unit is Percent or None + return -1; +} + +bool +PartSize::operator< ( const PartSize& other ) const +{ + if ( ( m_unit == SizeUnit::None || other.m_unit == SizeUnit::None ) || + ( m_unit == SizeUnit::Percent && other.m_unit != SizeUnit::Percent ) || + ( m_unit != SizeUnit::Percent && other.m_unit == SizeUnit::Percent ) ) + return false; + + switch ( m_unit ) + { + case SizeUnit::Percent: + return ( m_value < other.m_value ); + case SizeUnit::Byte: + case SizeUnit::KiB: + case SizeUnit::MiB: + case SizeUnit::GiB: + return ( toBytes() < other.toBytes () ); + } + + return false; +} + +bool +PartSize::operator> ( const PartSize& other ) const +{ + if ( ( m_unit == SizeUnit::None || other.m_unit == SizeUnit::None ) || + ( m_unit == SizeUnit::Percent && other.m_unit != SizeUnit::Percent ) || + ( m_unit != SizeUnit::Percent && other.m_unit == SizeUnit::Percent ) ) + return false; + + switch ( m_unit ) + { + case SizeUnit::Percent: + return ( m_value > other.m_value ); + case SizeUnit::Byte: + case SizeUnit::KiB: + case SizeUnit::MiB: + case SizeUnit::GiB: + return ( toBytes() > other.toBytes () ); + } + + return false; +} + +bool +PartSize::operator== ( const PartSize& other ) const +{ + if ( ( m_unit == SizeUnit::None || other.m_unit == SizeUnit::None ) || + ( m_unit == SizeUnit::Percent && other.m_unit != SizeUnit::Percent ) || + ( m_unit != SizeUnit::Percent && other.m_unit == SizeUnit::Percent ) ) + return false; + + switch ( m_unit ) + { + case SizeUnit::Percent: + return ( m_value == other.m_value ); + case SizeUnit::Byte: + case SizeUnit::KiB: + case SizeUnit::MiB: + case SizeUnit::GiB: + return ( toBytes() == other.toBytes () ); + } + + return false; +} + QString convenienceName( const Partition* const candidate ) { diff --git a/src/modules/partition/core/PartUtils.h b/src/modules/partition/core/PartUtils.h index 9b4efeec9..2e1420006 100644 --- a/src/modules/partition/core/PartUtils.h +++ b/src/modules/partition/core/PartUtils.h @@ -23,6 +23,7 @@ #include "OsproberEntry.h" #include "utils/Units.h" +#include "utils/NamedSuffix.h" // KPMcore #include @@ -37,15 +38,77 @@ namespace PartUtils { using CalamaresUtils::MiBtoBytes; -enum SizeUnit +enum class SizeUnit { - Percent = 0, + None, + Percent, Byte, KiB, MiB, GiB }; +/** @brief Partition size expressions + * + * Sizes can be specified in bytes, KiB, MiB, GiB or percent (of + * the available drive space are on). This class handles parsing + * of such strings from the config file. + */ +class PartSize : public NamedSuffix +{ +public: + PartSize() : NamedSuffix() { }; + PartSize( int v, unit_t u ) : NamedSuffix( v, u ) { }; + PartSize( const QString& ); + + bool isValid() const + { + return ( unit() != SizeUnit::None ) && ( value() > 0 ); + } + + bool operator< ( const PartSize& other ) const; + bool operator> ( const PartSize& other ) const; + bool operator== ( const PartSize& other ) const; + + /** @brief Convert the size to the number of sectors @p totalSectors . + * + * Each sector has size @p sectorSize, for converting sizes in Bytes, + * KiB, MiB or GiB to sector counts. + * + * @return the number of sectors needed, or -1 for invalid sizes. + */ + qint64 toSectors( qint64 totalSectors, qint64 sectorSize ) const; + + /** @brief Convert the size to bytes. + * + * The device's sectors count @p totalSectors and sector size + * @p sectoreSize are used to calculated the total size, which + * is then used to calculate the size when using Percent. + * + * @return the size in bytes, or -1 for invalid sizes. + */ + qint64 toBytes( qint64 totalSectors, qint64 sectorSize ) const; + + /** @brief Convert the size to bytes. + * + * Total size @p totalBytes is needed for sizes in Percent. This + * parameter is unused in any other case. + * + * @return the size in bytes, or -1 for invalid sizes. + */ + qint64 toBytes( qint64 totalBytes ) const; + + /** @brief Convert the size to bytes. + * + * This method is only valid for sizes in Bytes, KiB, MiB or GiB. + * It will return -1 in any other case. + * + * @return the size in bytes, or -1 if it cannot be calculated. + */ + qint64 toBytes() const; +}; + + /** * @brief Provides a nice human-readable name for @p candidate *