LCOV - code coverage report
Current view: top level - advgetopt - validator_size.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 118 118 100.0 %
Date: 2022-07-15 17:40:56 Functions: 10 11 90.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2006-2022  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           2 : 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           2 : 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           8 :     for(auto r : flag_list)
     132             :     {
     133           5 :         if(r == "si")
     134             :         {
     135           2 :             f_flags &= ~VALIDATOR_SIZE_POWER_OF_TWO;
     136             :         }
     137           4 :         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             :     }
     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      485591 : bool validator_size::validate(std::string const & value) const
     180             : {
     181             :     using namespace snapdev::literals;
     182      485591 :     __int128 result(0_int128);
     183      485591 :     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             :  * * "KiB" -- 1024^1 bytes
     203             :  * * "MiB" -- 1024^2 bytes
     204             :  * * "GiB" -- 1024^3 bytes
     205             :  * * "TiB" -- 1024^4 bytes
     206             :  * * "PiB" -- 1024^5 bytes
     207             :  * * "EiB" -- 1024^6 bytes
     208             :  * * "ZiB" -- 1024^7 bytes
     209             :  * * "YiB" -- 1024^8 bytes
     210             :  *
     211             :  * The suffix capitalization is not important since we can always distinguish
     212             :  * both types (power of 1000 or 1024). The 'B' represents bytes either way so
     213             :  * it does not need to be dinstinguished.
     214             :  *
     215             :  * In legacy mode (VALIDATOR_SIZE_POWER_OF_TWO flag set), the 1024 power
     216             :  * is always used.
     217             :  *
     218             :  * The final result is an integer representing bytes. If you use a decimal
     219             :  * number, it will be rounded down (floor). So "1.9B" returns 1. A decimal
     220             :  * number is practical for larger sizes such as "1.3GiB".
     221             :  *
     222             :  * \note
     223             :  * The result is returned in a 128 bit number because Zeta and Yeta values
     224             :  * do not fit in 64 bits.
     225             :  *
     226             :  * \todo
     227             :  * We may want to support full names instead of just the minimal abbreviated
     228             :  * suffixes (i.e. "3 bytes" fails). Also we could support bits but that is
     229             :  * \em complicated because the only difference is whether you use upper or
     230             :  * lower case characters (i.e. kB is kilo bytes, kb is kilo bits).
     231             :  *
     232             :  * \param[in] value  The value to be converted to a size.
     233             :  * \param[in] flags  The flags to determine how to interpret the suffix.
     234             :  * \param[out] result  The resulting size in bits or bytes.
     235             :  *
     236             :  * \return true if the conversion succeeded.
     237             :  */
     238             : #pragma GCC diagnostic push
     239             : #pragma GCC diagnostic ignored "-Wpedantic"
     240      809591 : bool validator_size::convert_string(
     241             :           std::string const & value
     242             :         , flag_t flags
     243             :         , __int128 & result)
     244             : {
     245             :     using namespace snapdev::literals;
     246             : 
     247             :     // determine the factor by checking the suffix
     248             :     //
     249      809591 :     __int128 factor(1_int128);
     250      809591 :     std::string::size_type pos(value.length());
     251     8545675 :     for(; pos > 0; --pos)
     252             :     {
     253     4677628 :         char c(value[pos - 1]);
     254     4677628 :         if(c >= '0'
     255     2653694 :         && c <= '9'
     256     3868042 :         || c == '.')
     257             :         {
     258             :             break;
     259             :         }
     260             :     }
     261      809591 :     if(pos == 0)
     262             :     {
     263           5 :         return false;
     264             :     }
     265             :     __int128 const base(
     266      809586 :         (flags & VALIDATOR_SIZE_POWER_OF_TWO) != 0
     267      809586 :             ? 1024_int128
     268      809586 :             : 1000_int128);
     269     1619172 :     std::string const number(value.substr(0, pos));
     270     2833516 :     for(; pos < value.length() && isspace(value[pos]); ++pos);
     271      809586 :     if(pos < value.length())
     272             :     {
     273             :         // copy and force lowercase (although the case is important
     274             :         // when writing such a measurement, it does not matter when
     275             :         // testing here)
     276             :         //
     277     1529207 :         std::string suffix;
     278     4452818 :         for(; pos < value.length(); ++pos)
     279             :         {
     280     3688208 :             if(value[pos] >= 'A'
     281     1844104 :             && value[pos] <= 'Z')
     282             :             {
     283     1439241 :                 suffix += value[pos] + 0x20;
     284             :             }
     285             :             else
     286             :             {
     287      404863 :                 suffix += value[pos];
     288             :             }
     289             :         }
     290             : 
     291      764610 :         switch(suffix[0])
     292             :         {
     293       44978 :         case 'b':
     294       44978 :             if(suffix != "b")
     295             :             {
     296           1 :                 return false;
     297             :             }
     298       44977 :             break;
     299             : 
     300       89953 :         case 'e':
     301       89953 :             if(suffix == "eb")
     302             :             {
     303       44976 :                 factor = snapdev::pow(base, 6);
     304             :             }
     305       44977 :             else if(suffix == "eib")
     306             :             {
     307       44976 :                 factor = snapdev::pow(1024_int128, 6);
     308             :             }
     309             :             else
     310             :             {
     311           1 :                 return false;
     312             :             }
     313       89952 :             break;
     314             : 
     315       89954 :         case 'g':
     316       89954 :             if(suffix == "gb")
     317             :             {
     318       44976 :                 factor = snapdev::pow(base, 3);
     319             :             }
     320       44978 :             else if(suffix == "gib")
     321             :             {
     322       44977 :                 factor = snapdev::pow(1024_int128, 3);
     323             :             }
     324             :             else
     325             :             {
     326           1 :                 return false;
     327             :             }
     328       89953 :             break;
     329             : 
     330       89954 :         case 'k':
     331       89954 :             if(suffix == "kb")
     332             :             {
     333       44976 :                 factor = base;
     334             :             }
     335       44978 :             else if(suffix == "kib")
     336             :             {
     337       44977 :                 factor = 1024_int128;
     338             :             }
     339             :             else
     340             :             {
     341           1 :                 return false;
     342             :             }
     343       89953 :             break;
     344             : 
     345       89955 :         case 'm':
     346       89955 :             if(suffix == "mb")
     347             :             {
     348       44976 :                 factor = snapdev::pow(base, 2);
     349             :             }
     350       44979 :             else if(suffix == "mib")
     351             :             {
     352       44977 :                 factor = snapdev::pow(1024_int128, 2);
     353             :             }
     354             :             else
     355             :             {
     356           2 :                 return false;
     357             :             }
     358       89953 :             break;
     359             : 
     360       89954 :         case 'p':
     361       89954 :             if(suffix == "pb")
     362             :             {
     363       44976 :                 factor = snapdev::pow(base, 5);
     364             :             }
     365       44978 :             else if(suffix == "pib")
     366             :             {
     367       44977 :                 factor = snapdev::pow(1024_int128, 5);
     368             :             }
     369             :             else
     370             :             {
     371           1 :                 return false;
     372             :             }
     373       89953 :             break;
     374             : 
     375       89954 :         case 't':
     376       89954 :             if(suffix == "tb")
     377             :             {
     378       44976 :                 factor = snapdev::pow(base, 4);
     379             :             }
     380       44978 :             else if(suffix == "tib")
     381             :             {
     382       44976 :                 factor = snapdev::pow(1024_int128, 4);
     383             :             }
     384             :             else
     385             :             {
     386           2 :                 return false;
     387             :             }
     388       89952 :             break;
     389             : 
     390       89953 :         case 'y':
     391       89953 :             if(suffix == "yb")
     392             :             {
     393       44976 :                 factor = snapdev::pow(base, 8);
     394             :             }
     395       44977 :             else if(suffix == "yib")
     396             :             {
     397       44976 :                 factor = snapdev::pow(1024_int128, 8);
     398             :             }
     399             :             else
     400             :             {
     401           1 :                 return false;
     402             :             }
     403       89952 :             break;
     404             : 
     405       89953 :         case 'z':
     406       89953 :             if(suffix == "zb")
     407             :             {
     408       44976 :                 factor = snapdev::pow(base, 7);
     409             :             }
     410       44977 :             else if(suffix == "zib")
     411             :             {
     412       44976 :                 factor = snapdev::pow(1024_int128, 7);
     413             :             }
     414             :             else
     415             :             {
     416           1 :                 return false;
     417             :             }
     418       89952 :             break;
     419             : 
     420           2 :         default:
     421           2 :             return false;
     422             : 
     423             :         }
     424             :     }
     425             : 
     426      809573 :     double base_number(0.0);
     427      809573 :     if(!validator_double::convert_string(number, base_number))
     428             :     {
     429           5 :         return false;
     430             :     }
     431             : 
     432             :     // TODO: I think that even with the long double this will lose bits
     433      809568 :     result = static_cast<long double>(base_number) * factor;
     434             : 
     435      809568 :     return true;
     436             : }
     437             : #pragma GCC diagnostic pop
     438             : 
     439             : 
     440             : 
     441           6 : } // namespace advgetopt
     442             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13