LCOV - code coverage report
Current view: top level - advgetopt - validator_size.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 111 123 90.2 %
Date: 2022-05-26 21:41:34 Functions: 11 12 91.7 %
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             : // last include
      53             : //
      54             : #include    <snapdev/poison.h>
      55             : 
      56             : 
      57             : 
      58             : 
      59             : namespace advgetopt
      60             : {
      61             : 
      62             : 
      63             : 
      64             : namespace
      65             : {
      66             : 
      67             : 
      68             : 
      69           2 : class validator_size_factory
      70             :     : public validator_factory
      71             : {
      72             : public:
      73           2 :     validator_size_factory()
      74           2 :     {
      75           2 :         validator::register_validator(*this);
      76           2 :     }
      77             : 
      78           4 :     virtual std::string get_name() const override
      79             :     {
      80           4 :         return std::string("size");
      81             :     }
      82             : 
      83           3 :     virtual std::shared_ptr<validator> create(string_list_t const & data) const override
      84             :     {
      85           3 :         return std::make_shared<validator_size>(data);
      86             :     }
      87             : };
      88             : 
      89           2 : validator_size_factory      g_validator_size_factory;
      90             : 
      91             : 
      92             : 
      93             : #pragma GCC diagnostic push
      94             : #pragma GCC diagnostic ignored "-Wpedantic"
      95      313866 : constexpr __int128 power128(__int128 value, int power)
      96             : {
      97      313866 :     __int128 result(value);
      98     1569330 :     for(--power; power > 0; --power)
      99             :     {
     100     1255464 :         result *= value;
     101             :     }
     102      313866 :     return result;
     103             : }
     104             : #pragma GCC diagnostic pop
     105             : 
     106             : 
     107             : 
     108             : } // no name namespace
     109             : 
     110             : 
     111             : 
     112             : 
     113             : 
     114             : /** \brief Initialize the size validator.
     115             :  *
     116             :  * The constructor accepts a string defining the acceptable sizes.
     117             :  *
     118             :  * The string uses the following format:
     119             :  *
     120             :  * \code
     121             :  *    start: flags
     122             :  *         | start flags
     123             :  *
     124             :  *    flags: 'si'
     125             :  *         | 'legacy'
     126             :  * \endcode
     127             :  *
     128             :  * 'si' stands for "Systeme International" (French for International
     129             :  * System of Units); this means "1kB" will stand for "1000 bytes".
     130             :  *
     131             :  * 'legacy' means that one kilo bytes will be represented by 1024 bytes.
     132             :  * So "1kB" with the legacy flag turned on represents "1024 bytes".
     133             :  *
     134             :  * The 'si' and 'legacy' flags are exclusive, the last one will be effective.
     135             :  *
     136             :  * \param[in] flag_list  The flags used to define how to interpret the data.
     137             :  */
     138           3 : validator_size::validator_size(string_list_t const & flag_list)
     139             : {
     140           5 :     for(auto r : flag_list)
     141             :     {
     142           2 :         if(r == "si")
     143             :         {
     144           1 :             f_flags &= ~VALIDATOR_SIZE_POWER_OF_TWO;
     145             :         }
     146           1 :         else if(r == "legacy")
     147             :         {
     148           1 :             f_flags |= VALIDATOR_SIZE_POWER_OF_TWO;
     149             :         }
     150             :         else
     151             :         {
     152           0 :             cppthread::log << cppthread::log_level_t::error
     153           0 :                            << r
     154           0 :                            << " is not a valid flag for the size validator."
     155           0 :                            << cppthread::end;
     156           0 :             continue;
     157             :         }
     158             :     }
     159           3 : }
     160             : 
     161             : 
     162             : /** \brief Return the name of this validator.
     163             :  *
     164             :  * This function returns "size".
     165             :  *
     166             :  * \return "size".
     167             :  */
     168           3 : std::string validator_size::name() const
     169             : {
     170           3 :     return std::string("size");
     171             : }
     172             : 
     173             : 
     174             : /** \brief Determine whether value is a valid size.
     175             :  *
     176             :  * This function verifies that the specified value is a valid size.
     177             :  *
     178             :  * It makes sures that the value is a valid decimal number which optionally
     179             :  * starts with a sign (`[-+]?`) and is optionally followed by a known
     180             :  * measurement suffix.
     181             :  *
     182             :  * \param[in] value  The value to validate.
     183             :  *
     184             :  * \return true if the value validates.
     185             :  */
     186             : #pragma GCC diagnostic push
     187             : #pragma GCC diagnostic ignored "-Wpedantic"
     188      483108 : bool validator_size::validate(std::string const & value) const
     189             : {
     190      483108 :     __int128 result(0);
     191      483108 :     return convert_string(value, f_flags, result);
     192             : }
     193             : #pragma GCC diagnostic pop
     194             : 
     195             : 
     196             : /** \brief Convert a string to a large integer (128 bits) value representing a size.
     197             :  *
     198             :  * This function is used to convert a string to a double representing a
     199             :  * size. The size can be specified with one of the following suffixes:
     200             :  *
     201             :  * * "B" -- 1000^0 bytes
     202             :  * * "kB" -- 1000^1 bytes
     203             :  * * "MB" -- 1000^2 bytes
     204             :  * * "GB" -- 1000^3 bytes
     205             :  * * "TB" -- 1000^4 bytes
     206             :  * * "PB" -- 1000^5 bytes
     207             :  * * "EB" -- 1000^6 bytes
     208             :  * * "ZB" -- 1000^7 bytes
     209             :  * * "YB" -- 1000^8 bytes
     210             :  * * "KiB" -- 1024^1 bytes
     211             :  * * "MiB" -- 1024^2 bytes
     212             :  * * "GiB" -- 1024^3 bytes
     213             :  * * "TiB" -- 1024^4 bytes
     214             :  * * "PiB" -- 1024^5 bytes
     215             :  * * "EiB" -- 1024^6 bytes
     216             :  * * "ZiB" -- 1024^7 bytes
     217             :  * * "YiB" -- 1024^8 bytes
     218             :  *
     219             :  * The suffix capitalization is not important since we can always distinguish
     220             :  * both types (power of 1000 or 1024). The 'B' represents bytes either way so
     221             :  * it does not need to be dinstinguished.
     222             :  *
     223             :  * In legacy mode (VALIDATOR_SIZE_POWER_OF_TWO flag set), the 1024 power
     224             :  * is always used.
     225             :  *
     226             :  * The final result is an integer representing bytes. If you use a decimal
     227             :  * number, it will be rounded down. So "1.9B" returns 1. A decimal number
     228             :  * is practical for larger sizes such as writing "1.3GiB".
     229             :  *
     230             :  * \note
     231             :  * The number is returned in a 128 bit number because Zeta and Yeta numbers
     232             :  * do not fit in 64 bits.
     233             :  *
     234             :  * \param[in] value  The value to be converted to a size.
     235             :  * \param[in] flags  The flags to determine how to interpret the suffix.
     236             :  * \param[out] result  The resulting size in bits or bytes.
     237             :  *
     238             :  * \return true if the conversion succeeded.
     239             :  */
     240             : #pragma GCC diagnostic push
     241             : #pragma GCC diagnostic ignored "-Wpedantic"
     242      807108 : bool validator_size::convert_string(
     243             :           std::string const & value
     244             :         , flag_t flags
     245             :         , __int128 & result)
     246             : {
     247             :     // determine the factor by checking the suffix
     248             :     //
     249      807108 :     __int128 factor(1);
     250      807108 :     std::string::size_type pos(value.length());
     251     8519400 :     for(; pos > 0; --pos)
     252             :     {
     253     4663242 :         char c(value[pos - 1]);
     254     4663242 :         if(c >= '0'
     255     2645520 :         && c <= '9'
     256     3856146 :         || c == '.')
     257             :         {
     258             :             break;
     259             :         }
     260             :     }
     261      807108 :     if(pos == 0)
     262             :     {
     263          12 :         return false;
     264             :     }
     265      807096 :     __int128 const base(
     266      807096 :         (flags & VALIDATOR_SIZE_POWER_OF_TWO) != 0
     267      807096 :             ? 1024
     268             :             : 1000);
     269     1614192 :     std::string const number(value.substr(0, pos));
     270     2824812 :     for(; pos < value.length() && isspace(value[pos]); ++pos);
     271      807096 :     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     1524504 :         std::string suffix;
     278     4439076 :         for(; pos < value.length(); ++pos)
     279             :         {
     280     3676818 :             if(value[pos] >= 'A'
     281     1838409 :             && value[pos] <= 'Z')
     282             :             {
     283     1434816 :                 suffix += value[pos] + 0x20;
     284             :             }
     285             :             else
     286             :             {
     287      403593 :                 suffix += value[pos];
     288             :             }
     289             :         }
     290             : 
     291      762258 :         switch(suffix[0])
     292             :         {
     293       44841 :         case 'b':
     294       44841 :             if(suffix != "b")
     295             :             {
     296           3 :                 return false;
     297             :             }
     298       44838 :             break;
     299             : 
     300       89679 :         case 'e':
     301       89679 :             if(suffix == "eb")
     302             :             {
     303       44838 :                 factor = base * base * base * base * base * base;
     304             :             }
     305       44841 :             else if(suffix == "eib")
     306             :             {
     307       44838 :                 factor = power128(1024, 6);
     308             :             }
     309             :             else
     310             :             {
     311           3 :                 return false;
     312             :             }
     313       89676 :             break;
     314             : 
     315       89679 :         case 'g':
     316       89679 :             if(suffix == "gb")
     317             :             {
     318       44838 :                 factor = base * base * base;
     319             :             }
     320       44841 :             else if(suffix == "gib")
     321             :             {
     322       44838 :                 factor = power128(1024, 3);
     323             :             }
     324             :             else
     325             :             {
     326           3 :                 return false;
     327             :             }
     328       89676 :             break;
     329             : 
     330       89676 :         case 'k':
     331       89676 :             if(suffix == "kb")
     332             :             {
     333       44838 :                 factor = base;
     334             :             }
     335       44838 :             else if(suffix == "kib")
     336             :             {
     337       44838 :                 factor = 1024;
     338             :             }
     339             :             else
     340             :             {
     341           0 :                 return false;
     342             :             }
     343       89676 :             break;
     344             : 
     345       89676 :         case 'm':
     346       89676 :             if(suffix == "mb")
     347             :             {
     348       44838 :                 factor = base * base;
     349             :             }
     350       44838 :             else if(suffix == "mib")
     351             :             {
     352       44838 :                 factor = power128(1024, 2);
     353             :             }
     354             :             else
     355             :             {
     356           0 :                 return false;
     357             :             }
     358       89676 :             break;
     359             : 
     360       89676 :         case 'p':
     361       89676 :             if(suffix == "pb")
     362             :             {
     363       44838 :                 factor = base * base * base * base * base;
     364             :             }
     365       44838 :             else if(suffix == "pib")
     366             :             {
     367       44838 :                 factor = power128(1024, 5);
     368             :             }
     369             :             else
     370             :             {
     371           0 :                 return false;
     372             :             }
     373       89676 :             break;
     374             : 
     375       89676 :         case 't':
     376       89676 :             if(suffix == "tb")
     377             :             {
     378       44838 :                 factor = base * base * base * base;
     379             :             }
     380       44838 :             else if(suffix == "tib")
     381             :             {
     382       44838 :                 factor = power128(1024, 4);
     383             :             }
     384             :             else
     385             :             {
     386           0 :                 return false;
     387             :             }
     388       89676 :             break;
     389             : 
     390       89676 :         case 'y':
     391       89676 :             if(suffix == "yb")
     392             :             {
     393       44838 :                 factor = base * base * base * base * base * base * base * base;
     394             :             }
     395       44838 :             else if(suffix == "yib")
     396             :             {
     397       44838 :                 factor = power128(1024, 8);
     398             :             }
     399             :             else
     400             :             {
     401           0 :                 return false;
     402             :             }
     403       89676 :             break;
     404             : 
     405       89676 :         case 'z':
     406       89676 :             if(suffix == "zb")
     407             :             {
     408       44838 :                 factor = base * base * base * base * base * base * base;
     409             :             }
     410       44838 :             else if(suffix == "zib")
     411             :             {
     412       44838 :                 factor = power128(1024, 7);
     413             :             }
     414             :             else
     415             :             {
     416           0 :                 return false;
     417             :             }
     418       89676 :             break;
     419             : 
     420           3 :         default:
     421           3 :             return false;
     422             : 
     423             :         }
     424             :     }
     425             : 
     426      807084 :     double base_number(0.0);
     427      807084 :     if(!validator_double::convert_string(number, base_number))
     428             :     {
     429           0 :         return false;
     430             :     }
     431             : 
     432             :     // TODO: I think that even with the long double this will lose bits
     433      807084 :     result = static_cast<long double>(base_number) * factor;
     434             : 
     435      807084 :     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