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

Generated by: LCOV version 1.13