LCOV - code coverage report
Current view: top level - advgetopt - validator_size.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 130 132 98.5 %
Date: 2024-10-05 13:34:54 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2006-2024  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/advgetopt
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      19             : 
      20             : /** \file
      21             :  * \brief Implementation of the size validator.
      22             :  *
      23             :  * The advgetopt allows for validating the input parameters automatically.
      24             :  * This one validator checks whether the input represents what is considered
      25             :  * a valid size of bits or bytes.
      26             :  *
      27             :  * This includes a floating point number followed by a suffix such as "kB"
      28             :  * or "Gb" or "TiB".
      29             :  *
      30             :  * \note
      31             :  * The size can also represents bits, even though it was written to read
      32             :  * bytes. If your command line option is expecting a size in bits, this works
      33             :  * just as expected.
      34             :  *
      35             :  * The size conversions are based on the International System of Units (SI).
      36             :  *
      37             :  * See: https://en.wikipedia.org/wiki/Kilobyte
      38             :  */
      39             : 
      40             : // self
      41             : //
      42             : #include    "advgetopt/validator_size.h"
      43             : 
      44             : #include    "advgetopt/validator_double.h"
      45             : 
      46             : 
      47             : // cppthread
      48             : //
      49             : #include    <cppthread/log.h>
      50             : 
      51             : 
      52             : // snapdev
      53             : //
      54             : #include    <snapdev/int128_literal.h>
      55             : #include    <snapdev/math.h>
      56             : 
      57             : 
      58             : // last include
      59             : //
      60             : #include    <snapdev/poison.h>
      61             : 
      62             : 
      63             : 
      64             : 
      65             : namespace advgetopt
      66             : {
      67             : 
      68             : 
      69             : 
      70             : namespace
      71             : {
      72             : 
      73             : 
      74             : 
      75             : class validator_size_factory
      76             :     : public validator_factory
      77             : {
      78             : public:
      79           2 :     validator_size_factory()
      80           2 :     {
      81           2 :         validator::register_validator(*this);
      82           2 :     }
      83             : 
      84           4 :     virtual std::string get_name() const override
      85             :     {
      86           4 :         return std::string("size");
      87             :     }
      88             : 
      89           4 :     virtual std::shared_ptr<validator> create(string_list_t const & data) const override
      90             :     {
      91           4 :         return std::make_shared<validator_size>(data);
      92             :     }
      93             : };
      94             : 
      95             : validator_size_factory      g_validator_size_factory;
      96             : 
      97             : 
      98             : 
      99             : } // no name namespace
     100             : 
     101             : 
     102             : 
     103             : 
     104             : 
     105             : /** \brief Initialize the size validator.
     106             :  *
     107             :  * The constructor accepts a string defining the acceptable sizes.
     108             :  *
     109             :  * The string uses the following format:
     110             :  *
     111             :  * \code
     112             :  *    start: flags
     113             :  *         | start flags
     114             :  *
     115             :  *    flags: 'si'
     116             :  *         | 'legacy'
     117             :  * \endcode
     118             :  *
     119             :  * 'si' stands for "Systeme International" (French for International
     120             :  * System of Units); this means "1kB" will stand for "1000 bytes".
     121             :  *
     122             :  * 'legacy' means that one kilo bytes will be represented by 1024 bytes.
     123             :  * So "1kB" with the legacy flag turned on represents "1024 bytes".
     124             :  *
     125             :  * The 'si' and 'legacy' flags are exclusive, the last one will be effective.
     126             :  *
     127             :  * \param[in] flag_list  The flags used to define how to interpret the data.
     128             :  */
     129           4 : validator_size::validator_size(string_list_t const & flag_list)
     130             : {
     131           9 :     for(auto r : flag_list)
     132             :     {
     133           5 :         if(r == "si")
     134             :         {
     135           2 :             f_flags &= ~VALIDATOR_SIZE_POWER_OF_TWO;
     136             :         }
     137           3 :         else if(r == "legacy")
     138             :         {
     139           2 :             f_flags |= VALIDATOR_SIZE_POWER_OF_TWO;
     140             :         }
     141             :         else
     142             :         {
     143           2 :             cppthread::log << cppthread::log_level_t::error
     144           1 :                            << r
     145           1 :                            << " is not a valid flag for the size validator."
     146           2 :                            << cppthread::end;
     147           1 :             continue;
     148             :         }
     149           5 :     }
     150           4 : }
     151             : 
     152             : 
     153             : /** \brief Return the name of this validator.
     154             :  *
     155             :  * This function returns "size".
     156             :  *
     157             :  * \return "size".
     158             :  */
     159           3 : std::string validator_size::name() const
     160             : {
     161           3 :     return std::string("size");
     162             : }
     163             : 
     164             : 
     165             : /** \brief Determine whether value is a valid size.
     166             :  *
     167             :  * This function verifies that the specified value is a valid size.
     168             :  *
     169             :  * It makes sures that the value is a valid decimal number which optionally
     170             :  * starts with a sign (`[-+]?`) and is optionally followed by a known
     171             :  * measurement suffix.
     172             :  *
     173             :  * \param[in] value  The value to validate.
     174             :  *
     175             :  * \return true if the value validates.
     176             :  */
     177             : #pragma GCC diagnostic push
     178             : #pragma GCC diagnostic ignored "-Wpedantic"
     179      592307 : bool validator_size::validate(std::string const & value) const
     180             : {
     181             :     using namespace snapdev::literals;
     182      592307 :     __int128 result(0_int128);
     183     1184614 :     return convert_string(value, f_flags, result);
     184             : }
     185             : #pragma GCC diagnostic pop
     186             : 
     187             : 
     188             : /** \brief Convert a string to a large integer (128 bits) value representing a size.
     189             :  *
     190             :  * This function is used to convert a string to a double representing a
     191             :  * size. The size can be specified with one of the following suffixes:
     192             :  *
     193             :  * * "B" -- 1000^0 bytes
     194             :  * * "kB" -- 1000^1 bytes
     195             :  * * "MB" -- 1000^2 bytes
     196             :  * * "GB" -- 1000^3 bytes
     197             :  * * "TB" -- 1000^4 bytes
     198             :  * * "PB" -- 1000^5 bytes
     199             :  * * "EB" -- 1000^6 bytes
     200             :  * * "ZB" -- 1000^7 bytes
     201             :  * * "YB" -- 1000^8 bytes
     202             :  * * "RB" -- 1000^9 bytes
     203             :  * * "QB" -- 1000^10 bytes
     204             :  * * "KiB" -- 1024^1 bytes
     205             :  * * "MiB" -- 1024^2 bytes
     206             :  * * "GiB" -- 1024^3 bytes
     207             :  * * "TiB" -- 1024^4 bytes
     208             :  * * "PiB" -- 1024^5 bytes
     209             :  * * "EiB" -- 1024^6 bytes
     210             :  * * "ZiB" -- 1024^7 bytes
     211             :  * * "YiB" -- 1024^8 bytes
     212             :  * * "RiB" -- 1024^9 bytes
     213             :  * * "QiB" -- 1024^10 bytes
     214             :  *
     215             :  * The suffix capitalization is not important since we can always distinguish
     216             :  * both types (power of 1000 or 1024). The 'B' represents bytes either way so
     217             :  * it does not need to be dinstinguished.
     218             :  *
     219             :  * In legacy mode (VALIDATOR_SIZE_POWER_OF_TWO flag set), the 1024 power
     220             :  * is always used.
     221             :  *
     222             :  * The final result is an integer representing bytes. If you use a decimal
     223             :  * number, it will be rounded down (floor). So "1.9B" returns 1. A decimal
     224             :  * number is practical for larger sizes such as "1.3GiB".
     225             :  *
     226             :  * \note
     227             :  * The result is returned in a 128 bit number because Zeta and Yeta values
     228             :  * do not fit in 64 bits.
     229             :  *
     230             :  * \todo
     231             :  * We may want to support full names instead of just the minimal abbreviated
     232             :  * suffixes (i.e. "3 bytes" fails). Also we could support bits but that is
     233             :  * \em complicated because the only difference is whether you use upper or
     234             :  * lower case characters (i.e. kB is kilo bytes, kb is kilo bits).
     235             :  *
     236             :  * \param[in] value  The value to be converted to a size.
     237             :  * \param[in] flags  The flags to determine how to interpret the suffix.
     238             :  * \param[out] result  The resulting size in bits or bytes.
     239             :  *
     240             :  * \return true if the conversion succeeded.
     241             :  */
     242             : #pragma GCC diagnostic push
     243             : #pragma GCC diagnostic ignored "-Wpedantic"
     244      988307 : bool validator_size::convert_string(
     245             :           std::string const & value
     246             :         , flag_t flags
     247             :         , __int128 & result)
     248             : {
     249             :     using namespace snapdev::literals;
     250             : 
     251             :     // determine the factor by checking the suffix
     252             :     //
     253      988307 :     __int128 factor(1_int128);
     254      988307 :     std::string::size_type pos(value.length());
     255     5750145 :     for(; pos > 0; --pos)
     256             :     {
     257     5750140 :         char c(value[pos - 1]);
     258     5750140 :         if(c >= '0'
     259     3279416 :         && c <= '9'
     260     4761838 :         || c == '.')
     261             :         {
     262             :             break;
     263             :         }
     264             :     }
     265      988307 :     if(pos == 0)
     266             :     {
     267           5 :         return false;
     268             :     }
     269             :     __int128 const base(
     270      988302 :         (flags & VALIDATOR_SIZE_POWER_OF_TWO) != 0
     271      988302 :             ? 1024_int128
     272      660000 :             : 1000_int128);
     273      988302 :     std::string const number(value.substr(0, pos));
     274     3459022 :     for(; pos < value.length() && isspace(value[pos]); ++pos);
     275      988302 :     if(pos < value.length())
     276             :     {
     277             :         // copy and force lowercase (although the case is important
     278             :         // when writing such a measurement, it does not matter when
     279             :         // testing here)
     280             :         //
     281      943380 :         std::string suffix;
     282     3234490 :         for(; pos < value.length(); ++pos)
     283             :         {
     284     2291110 :             if(value[pos] >= 'A'
     285     2291110 :             && value[pos] <= 'Z')
     286             :             {
     287     1796889 :                 suffix += value[pos] + 0x20;
     288             :             }
     289             :             else
     290             :             {
     291      494221 :                 suffix += value[pos];
     292             :             }
     293             :         }
     294             : 
     295      943380 :         switch(suffix[0])
     296             :         {
     297       44924 :         case 'b':
     298       44924 :             if(suffix != "b")
     299             :             {
     300           1 :                 return false;
     301             :             }
     302       44923 :             break;
     303             : 
     304       89845 :         case 'e':
     305       89845 :             if(suffix == "eb")
     306             :             {
     307       44922 :                 factor = snapdev::pow(base, 6);
     308             :             }
     309       44923 :             else if(suffix == "eib")
     310             :             {
     311       44922 :                 factor = snapdev::pow(1024_int128, 6);
     312             :             }
     313             :             else
     314             :             {
     315           1 :                 return false;
     316             :             }
     317       89844 :             break;
     318             : 
     319       89846 :         case 'g':
     320       89846 :             if(suffix == "gb")
     321             :             {
     322       44922 :                 factor = snapdev::pow(base, 3);
     323             :             }
     324       44924 :             else if(suffix == "gib")
     325             :             {
     326       44923 :                 factor = snapdev::pow(1024_int128, 3);
     327             :             }
     328             :             else
     329             :             {
     330           1 :                 return false;
     331             :             }
     332       89845 :             break;
     333             : 
     334       89846 :         case 'k':
     335       89846 :             if(suffix == "kb")
     336             :             {
     337       44922 :                 factor = base;
     338             :             }
     339       44924 :             else if(suffix == "kib")
     340             :             {
     341       44923 :                 factor = 1024_int128;
     342             :             }
     343             :             else
     344             :             {
     345           1 :                 return false;
     346             :             }
     347       89845 :             break;
     348             : 
     349       89847 :         case 'm':
     350       89847 :             if(suffix == "mb")
     351             :             {
     352       44922 :                 factor = snapdev::pow(base, 2);
     353             :             }
     354       44925 :             else if(suffix == "mib")
     355             :             {
     356       44923 :                 factor = snapdev::pow(1024_int128, 2);
     357             :             }
     358             :             else
     359             :             {
     360           2 :                 return false;
     361             :             }
     362       89845 :             break;
     363             : 
     364       89846 :         case 'p':
     365       89846 :             if(suffix == "pb")
     366             :             {
     367       44922 :                 factor = snapdev::pow(base, 5);
     368             :             }
     369       44924 :             else if(suffix == "pib")
     370             :             {
     371       44923 :                 factor = snapdev::pow(1024_int128, 5);
     372             :             }
     373             :             else
     374             :             {
     375           1 :                 return false;
     376             :             }
     377       89845 :             break;
     378             : 
     379       89844 :         case 'q':
     380       89844 :             if(suffix == "qb")
     381             :             {
     382       44922 :                 factor = snapdev::pow(base, 10);
     383             :             }
     384       44922 :             else if(suffix == "qib")
     385             :             {
     386       44922 :                 factor = snapdev::pow(1024_int128, 10);
     387             :             }
     388             :             else
     389             :             {
     390           0 :                 return false;
     391             :             }
     392       89844 :             break;
     393             : 
     394       89844 :         case 'r':
     395       89844 :             if(suffix == "rb")
     396             :             {
     397       44922 :                 factor = snapdev::pow(base, 9);
     398             :             }
     399       44922 :             else if(suffix == "rib")
     400             :             {
     401       44922 :                 factor = snapdev::pow(1024_int128, 9);
     402             :             }
     403             :             else
     404             :             {
     405           0 :                 return false;
     406             :             }
     407       89844 :             break;
     408             : 
     409       89846 :         case 't':
     410       89846 :             if(suffix == "tb")
     411             :             {
     412       44922 :                 factor = snapdev::pow(base, 4);
     413             :             }
     414       44924 :             else if(suffix == "tib")
     415             :             {
     416       44922 :                 factor = snapdev::pow(1024_int128, 4);
     417             :             }
     418             :             else
     419             :             {
     420           2 :                 return false;
     421             :             }
     422       89844 :             break;
     423             : 
     424       89845 :         case 'y':
     425       89845 :             if(suffix == "yb")
     426             :             {
     427       44922 :                 factor = snapdev::pow(base, 8);
     428             :             }
     429       44923 :             else if(suffix == "yib")
     430             :             {
     431       44922 :                 factor = snapdev::pow(1024_int128, 8);
     432             :             }
     433             :             else
     434             :             {
     435           1 :                 return false;
     436             :             }
     437       89844 :             break;
     438             : 
     439       89845 :         case 'z':
     440       89845 :             if(suffix == "zb")
     441             :             {
     442       44922 :                 factor = snapdev::pow(base, 7);
     443             :             }
     444       44923 :             else if(suffix == "zib")
     445             :             {
     446       44922 :                 factor = snapdev::pow(1024_int128, 7);
     447             :             }
     448             :             else
     449             :             {
     450           1 :                 return false;
     451             :             }
     452       89844 :             break;
     453             : 
     454           2 :         default:
     455           2 :             return false;
     456             : 
     457             :         }
     458      943380 :     }
     459             : 
     460      988289 :     double base_number(0.0);
     461      988289 :     if(!validator_double::convert_string(number, base_number))
     462             :     {
     463           5 :         return false;
     464             :     }
     465             : 
     466             :     // TODO: I think that even with the long double this will lose bits
     467      988284 :     result = static_cast<long double>(base_number) * factor;
     468             : 
     469      988284 :     return true;
     470      988302 : }
     471             : #pragma GCC diagnostic pop
     472             : 
     473             : 
     474             : 
     475             : } // namespace advgetopt
     476             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14

Snap C++ | List of projects | List of versions