Ticket #1077: numeric.ipp.patch

File numeric.ipp.patch, 12.0 KB (added by Hartmut Kaiser, 15 years ago)

Patch fixing the integer overflow detection during parsing

  • numerics.

    old new  
    1111#define BOOST_SPIRIT_NUMERICS_IPP
    1212
    1313#include <cmath>
     14#include <limits>
    1415
    1516#if defined(BOOST_NO_STDC_NAMESPACE)
    1617#  define BOOST_SPIRIT_IMPL_STD_NS
     
    5051        //
    5152        //  Traits class for radix specific number conversion
    5253        //
    53         //      Test the validity of a single character:
     54        //      Convert a digit from character representation, ch, to binary
     55        //      representation, returned in val.
     56        //      Returns whether the conversion was successful.
    5457        //
    55         //          template<typename CharT> static bool is_valid(CharT ch);
    56         //
    57         //      Convert a digit from character representation to binary
    58         //      representation:
    59         //
    60         //          template<typename CharT> static int digit(CharT ch);
     58        //        template<typename CharT> static bool digit(CharT ch, T& val);
    6159        //
    6260        ///////////////////////////////////////////////////////////////////////
    6361        template<const int Radix>
     
    6765        template<>
    6866        struct radix_traits<2>
    6967        {
    70             template<typename CharT>
    71             static bool is_valid(CharT ch)
     68            template<typename CharT, typename T>
     69            static bool digit(CharT ch, T& val)
    7270            {
     71                val = ch - '0';
    7372                return ('0' == ch || '1' == ch);
    7473            }
    75 
    76             template<typename CharT>
    77             static int digit(CharT ch)
    78             {
    79                 return ch - '0';
    80             }
    8174        };
    8275
    8376        ////////////////////////////////// Octal
    8477        template<>
    8578        struct radix_traits<8>
    8679        {
    87             template<typename CharT>
    88             static bool is_valid(CharT ch)
     80            template<typename CharT, typename T>
     81            static bool digit(CharT ch, T& val)
    8982            {
     83                val = ch - '0';
    9084                return ('0' <= ch && ch <= '7');
    9185            }
    92 
    93             template<typename CharT>
    94             static int digit(CharT ch)
    95             {
    96                 return ch - '0';
    97             }
    9886        };
    9987
    10088        ////////////////////////////////// Decimal
    10189        template<>
    10290        struct radix_traits<10>
    10391        {
    104             template<typename CharT>
    105             static bool is_valid(CharT ch)
     92            template<typename CharT, typename T>
     93            static bool digit(CharT ch, T& val)
    10694            {
     95                val = ch - '0';
    10796                return impl::isdigit_(ch);
    10897            }
    109 
    110             template<typename CharT>
    111             static int digit(CharT ch)
    112             {
    113                 return ch - '0';
    114             }
    11598        };
    11699
    117100        ////////////////////////////////// Hexadecimal
    118101        template<>
    119102        struct radix_traits<16>
    120103        {
    121             template<typename CharT>
    122             static bool is_valid(CharT ch)
     104            template<typename CharT, typename T>
     105            static bool digit(CharT ch, T& val)
    123106            {
    124                 return impl::isxdigit_(ch);
    125             }
     107                if (radix_traits<10>::digit(ch, val))
     108                    return true;
    126109
    127             template<typename CharT>
    128             static int digit(CharT ch)
    129             {
    130                 if (impl::isdigit_(ch))
    131                     return ch - '0';
    132                 return impl::tolower_(ch) - 'a' + 10;
     110                CharT lc = impl::tolower_(ch);
     111                if ('a' <= lc && lc <= 'f')
     112                {
     113                    val = lc - 'a' + 10;
     114                    return true;
     115                }
     116                return false;
    133117            }
    134118        };
    135119
     
    161145        //
    162146        //      NOTE:
    163147        //              Returns a non-match, if the number to parse
    164         //              overflows (or underflows) the used integral type.
    165         //              Overflow (or underflow) is detected when an
    166         //              operation wraps a value from its maximum to its
    167         //              minimum (or vice-versa). For example, overflow
    168         //              occurs when the result of the expression n * x is
    169         //              less than n (assuming n is positive and x is
    170         //              greater than 1).
     148        //              overflows (or underflows) the used type.
    171149        //
    172150        //      BEWARE:
    173151        //              the parameters 'n' and 'count' should be properly
    174152        //              initialized before calling this function.
    175153        //
    176154        ///////////////////////////////////////////////////////////////////////
    177         template <int Radix>
     155        template <typename T, int Radix>
    178156        struct positive_accumulate
    179157        {
    180158            //  Use this accumulator if number is positive
    181 
    182             template <typename T>
    183             static bool check(T const& n, T const& prev)
     159            static bool add(T& n, T digit)
    184160            {
    185                 return n < prev;
    186             }
    187 
    188             template <typename T, typename CharT>
    189             static void add(T& n, CharT ch)
    190             {
    191                 n += radix_traits<Radix>::digit(ch);
     161                static T const max = (std::numeric_limits<T>::max)();
     162                static T const max_div_radix = max/Radix;
     163               
     164                if (n > max_div_radix)
     165                    return false;
     166                n *= Radix;
     167               
     168                if (n > max - digit)
     169                    return false;
     170                n += digit;
     171               
     172                return true;
    192173            }
    193174        };
    194 
    195         template <int Radix>
     175       
     176        template <typename T, int Radix>
    196177        struct negative_accumulate
    197178        {
    198179            //  Use this accumulator if number is negative
    199 
    200             template <typename T>
    201             static bool check(T const& n, T const& prev)
    202             {
    203                 return n > prev;
    204             }
    205 
    206             template <typename T, typename CharT>
    207             static void add(T& n, CharT ch)
     180            static bool add(T& n, T digit)
    208181            {
    209                 n -= radix_traits<Radix>::digit(ch);
    210             }
    211         };
    212 
    213         template <int Radix, typename Accumulate>
    214         struct extract_int_base
    215         {
    216             //  Common code for extract_int specializations
    217             template <typename ScannerT, typename T>
    218             static bool
    219             f(ScannerT& scan, T& n)
    220             {
    221                 T prev = n;
     182                typedef std::numeric_limits<T> num_limits;
     183                static T const min =
     184                    (!num_limits::is_integer && num_limits::is_signed && num_limits::has_denorm) ?
     185                    -(num_limits::max)() : (num_limits::min)();
     186                static T const min_div_radix = min/Radix;
     187           
     188                if (n < min_div_radix)
     189                    return false;
    222190                n *= Radix;
    223                 if (Accumulate::check(n, prev))
    224                     return false;   //  over/underflow!
    225                 prev = n;
    226                 Accumulate::add(n, *scan);
    227                 if (Accumulate::check(n, prev))
    228                     return false;   //  over/underflow!
     191               
     192                if (n < min + digit)
     193                    return false;
     194                n -= digit;
     195
    229196                return true;
    230197            }
    231198        };
    232199
    233         template <bool Bounded>
    234         struct extract_int_
    235         {
    236             template <
    237                 int Radix,
    238                 unsigned MinDigits,
    239                 int MaxDigits,
    240                 typename Accumulate
    241             >
    242             struct apply
    243             {
    244                 typedef extract_int_base<Radix, Accumulate> base;
    245                 typedef radix_traits<Radix> check;
    246 
    247                 template <typename ScannerT, typename T>
    248                 static bool
    249                 f(ScannerT& scan, T& n, std::size_t& count)
    250                 {
    251                     std::size_t i = 0;
    252                     for (; (i < MaxDigits) && !scan.at_end()
    253                         && check::is_valid(*scan);
    254                         ++i, ++scan, ++count)
    255                     {
    256                         if (!base::f(scan, n))
    257                             return false;   //  over/underflow!
    258                     }
    259                     return i >= MinDigits;
    260                 }
    261             };
    262         };
    263 
     200        template <int MaxDigits>
     201        inline bool allow_more_digits(std::size_t i)
     202        {
     203            return i < MaxDigits;
     204        }
     205       
    264206        template <>
    265         struct extract_int_<false>
     207        inline bool allow_more_digits<-1>(std::size_t)
    266208        {
    267             template <
    268                 int Radix,
    269                 unsigned MinDigits,
    270                 int MaxDigits,
    271                 typename Accumulate
    272             >
    273             struct apply
    274             {
    275                 typedef extract_int_base<Radix, Accumulate> base;
    276                 typedef radix_traits<Radix> check;
    277 
    278                 template <typename ScannerT, typename T>
    279                 static bool
    280                 f(ScannerT& scan, T& n, std::size_t& count)
    281                 {
    282                     std::size_t i = 0;
    283                     for (; !scan.at_end() && check::is_valid(*scan);
    284                         ++i, ++scan, ++count)
    285                     {
    286                         if (!base::f(scan, n))
    287                             return false;   //  over/underflow!
    288                     }
    289                     return i >= MinDigits;
    290                 }
    291             };
    292         };
    293 
     209            return true;
     210        }
     211       
    294212        //////////////////////////////////
    295213        template <
    296214            int Radix, unsigned MinDigits, int MaxDigits,
    297             typename Accumulate = positive_accumulate<Radix>
     215            typename Accumulate
    298216        >
    299217        struct extract_int
    300218        {
     
    302220            static bool
    303221            f(ScannerT& scan, T& n, std::size_t& count)
    304222            {
    305                 typedef typename extract_int_<(MaxDigits >= 0)>::template
    306                     apply<Radix, MinDigits, MaxDigits, Accumulate> extractor;
    307                 return extractor::f(scan, n, count);
     223                std::size_t i = 0;
     224                T digit;
     225                while( allow_more_digits<MaxDigits>(i) && !scan.at_end() &&
     226                    radix_traits<Radix>::digit(*scan, digit) )
     227                {
     228                    if (!Accumulate::add(n, digit))
     229                        return false; // Overflow
     230                    ++i, ++scan, ++count;
     231                }
     232                return i >= MinDigits;
    308233            }
    309234        };
    310235
     
    339264                    T n = 0;
    340265                    std::size_t count = 0;
    341266                    typename ScannerT::iterator_t save = scan.first;
    342                     if (extract_int<Radix, MinDigits, MaxDigits>::
    343                         f(scan, n, count))
     267                    if (extract_int<Radix, MinDigits, MaxDigits,
     268                        positive_accumulate<T, Radix> >::f(scan, n, count))
    344269                    {
    345270                        return scan.create_match(count, n, save, scan.first);
    346271                    }
     
    377302            parse(ScannerT const& scan) const
    378303            {
    379304                typedef extract_int<Radix, MinDigits, MaxDigits,
    380                     negative_accumulate<Radix> > extract_int_neg_t;
    381                 typedef extract_int<Radix, MinDigits, MaxDigits>
    382                     extract_int_pos_t;
     305                    negative_accumulate<T, Radix> > extract_int_neg_t;
     306                typedef extract_int<Radix, MinDigits, MaxDigits,
     307                    positive_accumulate<T, Radix> > extract_int_pos_t;
    383308
    384309                if (!scan.at_end())
    385310                {