LCOV - code coverage report
Current view: top level - opt/homebrew/include/boost/lexical_cast/detail - lcast_unsigned_converters.hpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 36 60 60.0 %
Date: 2026-06-25 07:23:43 Functions: 10 10 100.0 %

          Line data    Source code
       1             : // Copyright Kevlin Henney, 2000-2005.
       2             : // Copyright Alexander Nasonov, 2006-2010.
       3             : // Copyright Antony Polukhin, 2011-2025.
       4             : //
       5             : // Distributed under the Boost Software License, Version 1.0. (See
       6             : // accompanying file LICENSE_1_0.txt or copy at
       7             : // http://www.boost.org/LICENSE_1_0.txt)
       8             : //
       9             : // what:  lexical_cast custom keyword cast
      10             : // who:   contributed by Kevlin Henney,
      11             : //        enhanced with contributions from Terje Slettebo,
      12             : //        with additional fixes and suggestions from Gennaro Prota,
      13             : //        Beman Dawes, Dave Abrahams, Daryle Walker, Peter Dimov,
      14             : //        Alexander Nasonov, Antony Polukhin, Justin Viiret, Michael Hofmann,
      15             : //        Cheng Yang, Matthew Bradbury, David W. Birdsall, Pavel Korzh and other Boosters
      16             : // when:  November 2000, March 2003, June 2005, June 2006, March 2011 - 2014
      17             : 
      18             : #ifndef BOOST_LEXICAL_CAST_DETAIL_LCAST_UNSIGNED_CONVERTERS_HPP
      19             : #define BOOST_LEXICAL_CAST_DETAIL_LCAST_UNSIGNED_CONVERTERS_HPP
      20             : 
      21             : #include <boost/config.hpp>
      22             : #ifdef BOOST_HAS_PRAGMA_ONCE
      23             : #   pragma once
      24             : #endif
      25             : 
      26             : #include <climits>
      27             : #include <cstddef>
      28             : #include <string>
      29             : #include <cstring>
      30             : #include <cstdio>
      31             : #include <type_traits>
      32             : #include <boost/limits.hpp>
      33             : #include <boost/config/workaround.hpp>
      34             : #include <boost/lexical_cast/detail/type_traits.hpp>
      35             : 
      36             : 
      37             : #ifndef BOOST_NO_STD_LOCALE
      38             : #   include <locale>
      39             : #else
      40             : #   ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
      41             :         // Getting error at this point means, that your STL library is old/lame/misconfigured.
      42             :         // If nothing can be done with STL library, define BOOST_LEXICAL_CAST_ASSUME_C_LOCALE,
      43             :         // but beware: lexical_cast will understand only 'C' locale delimeters and thousands
      44             :         // separators.
      45             : #       error "Unable to use <locale> header. Define BOOST_LEXICAL_CAST_ASSUME_C_LOCALE to force "
      46             : #       error "boost::lexical_cast to use only 'C' locale during conversions."
      47             : #   endif
      48             : #endif
      49             : 
      50             : #include <boost/lexical_cast/detail/lcast_char_constants.hpp>
      51             : #include <boost/core/noncopyable.hpp>
      52             : 
      53             : namespace boost
      54             : {
      55             :     namespace detail // lcast_to_unsigned
      56             :     {
      57             :         template<class T>
      58             : #if defined(__clang__) && (__clang_major__ > 3 || __clang_minor__ > 6)
      59             :        __attribute__((no_sanitize("unsigned-integer-overflow")))
      60             : #endif
      61             :         inline
      62             :         typename boost::detail::lcast::make_unsigned<T>::type lcast_to_unsigned(const T value) noexcept {
      63             :             typedef typename boost::detail::lcast::make_unsigned<T>::type result_type;
      64             :             return value < 0
      65             :                 ? static_cast<result_type>(0u - static_cast<result_type>(value))
      66             :                 : static_cast<result_type>(value);
      67             :         }
      68             :     }
      69             : 
      70             :     namespace detail // lcast_put_unsigned
      71             :     {
      72             :         template <class Traits, class T, class CharT>
      73             :         class lcast_put_unsigned: boost::noncopyable {
      74             :             typedef typename Traits::int_type int_type;
      75             :             typename std::conditional<
      76             :                     (sizeof(unsigned) > sizeof(T))
      77             :                     , unsigned
      78             :                     , T
      79             :             >::type         m_value;
      80             :             CharT*          m_finish;
      81             :             CharT    const  m_czero;
      82             :             int_type const  m_zero;
      83             : 
      84             :         public:
      85             :             lcast_put_unsigned(const T n_param, CharT* finish) noexcept
      86             :                 : m_value(n_param), m_finish(finish)
      87             :                 , m_czero(lcast_char_constants<CharT>::zero), m_zero(Traits::to_int_type(m_czero))
      88             :             {
      89             : #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
      90             :                 static_assert(!std::numeric_limits<T>::is_signed, "");
      91             : #endif
      92             :             }
      93             : 
      94             :             CharT* convert() {
      95             : #ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
      96             :                 std::locale loc;
      97             :                 if (loc == std::locale::classic()) {
      98             :                     return main_convert_loop();
      99             :                 }
     100             : 
     101             :                 typedef std::numpunct<CharT> numpunct;
     102             :                 numpunct const& np = BOOST_USE_FACET(numpunct, loc);
     103             :                 std::string const grouping = np.grouping();
     104             :                 std::string::size_type const grouping_size = grouping.size();
     105             : 
     106             :                 if (!grouping_size || grouping[0] <= 0) {
     107             :                     return main_convert_loop();
     108             :                 }
     109             : 
     110             : #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
     111             :                 // Check that ulimited group is unreachable:
     112             :                 static_assert(std::numeric_limits<T>::digits10 < CHAR_MAX, "");
     113             : #endif
     114             :                 CharT const thousands_sep = np.thousands_sep();
     115             :                 std::string::size_type group = 0; // current group number
     116             :                 char last_grp_size = grouping[0];
     117             :                 char left = last_grp_size;
     118             : 
     119             :                 do {
     120             :                     if (left == 0) {
     121             :                         ++group;
     122             :                         if (group < grouping_size) {
     123             :                             char const grp_size = grouping[group];
     124             :                             last_grp_size = (grp_size <= 0 ? static_cast<char>(CHAR_MAX) : grp_size);
     125             :                         }
     126             : 
     127             :                         left = last_grp_size;
     128             :                         --m_finish;
     129             :                         Traits::assign(*m_finish, thousands_sep);
     130             :                     }
     131             : 
     132             :                     --left;
     133             :                 } while (main_convert_iteration());
     134             : 
     135             :                 return m_finish;
     136             : #else
     137             :                 return main_convert_loop();
     138             : #endif
     139             :             }
     140             : 
     141             :         private:
     142             :             inline bool main_convert_iteration() noexcept {
     143             :                 --m_finish;
     144             :                 int_type const digit = static_cast<int_type>(m_value % 10U);
     145             :                 Traits::assign(*m_finish, Traits::to_char_type(m_zero + digit));
     146             :                 m_value /= 10;
     147             :                 return !!m_value; // suppressing warnings
     148             :             }
     149             : 
     150             :             inline CharT* main_convert_loop() noexcept {
     151             :                 while (main_convert_iteration());
     152             :                 return m_finish;
     153             :             }
     154             :         };
     155             :     }
     156             : 
     157             :     namespace detail // lcast_ret_unsigned
     158             :     {
     159             :         template <class Traits, class T, class CharT>
     160             :         class lcast_ret_unsigned: boost::noncopyable {
     161             :             bool m_multiplier_overflowed;
     162             :             T m_multiplier;
     163             :             T& m_value;
     164             :             const CharT* const m_begin;
     165             :             const CharT* m_end;
     166             : 
     167             :         public:
     168       30000 :             lcast_ret_unsigned(T& value, const CharT* const begin, const CharT* end) noexcept
     169       15000 :                 : m_multiplier_overflowed(false), m_multiplier(1), m_value(value), m_begin(begin), m_end(end)
     170       15000 :             {
     171             : #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
     172             :                 static_assert(!std::numeric_limits<T>::is_signed, "");
     173             : 
     174             :                 // GCC when used with flag -std=c++0x may not have std::numeric_limits
     175             :                 // specializations for __int128 and unsigned __int128 types.
     176             :                 // Try compilation with -std=gnu++0x or -std=gnu++11.
     177             :                 //
     178             :                 // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40856
     179             :                 static_assert(std::numeric_limits<T>::is_specialized,
     180             :                     "std::numeric_limits are not specialized for integral type passed to boost::lexical_cast"
     181             :                 );
     182             : #endif
     183       30000 :             }
     184             : 
     185       15000 :             inline bool convert() {
     186       15000 :                 CharT const czero = lcast_char_constants<CharT>::zero;
     187       15000 :                 --m_end;
     188       15000 :                 m_value = static_cast<T>(0);
     189             : 
     190       15000 :                 if (m_begin > m_end || *m_end < czero || *m_end >= czero + 10)
     191           0 :                     return false;
     192       15000 :                 m_value = static_cast<T>(*m_end - czero);
     193       15000 :                 --m_end;
     194             : 
     195             : #ifdef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
     196             :                 return main_convert_loop();
     197             : #else
     198       15000 :                 std::locale loc;
     199       15000 :                 if (loc == std::locale::classic()) {
     200       15000 :                     return main_convert_loop();
     201             :                 }
     202             : 
     203             :                 typedef std::numpunct<CharT> numpunct;
     204           0 :                 numpunct const& np = BOOST_USE_FACET(numpunct, loc);
     205           0 :                 std::string const& grouping = np.grouping();
     206           0 :                 std::string::size_type const grouping_size = grouping.size();
     207             : 
     208             :                 /* According to Programming languages - C++
     209             :                  * we MUST check for correct grouping
     210             :                  */
     211           0 :                 if (!grouping_size || grouping[0] <= 0) {
     212           0 :                     return main_convert_loop();
     213             :                 }
     214             : 
     215           0 :                 unsigned char current_grouping = 0;
     216           0 :                 CharT const thousands_sep = np.thousands_sep();
     217           0 :                 char remained = static_cast<char>(grouping[current_grouping] - 1);
     218             : 
     219           0 :                 for (;m_end >= m_begin; --m_end)
     220             :                 {
     221           0 :                     if (remained) {
     222           0 :                         if (!main_convert_iteration()) {
     223           0 :                             return false;
     224             :                         }
     225           0 :                         --remained;
     226           0 :                     } else {
     227           0 :                         if ( !Traits::eq(*m_end, thousands_sep) ) //|| begin == end ) return false;
     228             :                         {
     229             :                             /*
     230             :                              * According to Programming languages - C++
     231             :                              * Digit grouping is checked. That is, the positions of discarded
     232             :                              * separators is examined for consistency with
     233             :                              * use_facet<numpunct<charT> >(loc ).grouping()
     234             :                              *
     235             :                              * BUT what if there is no separators at all and grouping()
     236             :                              * is not empty? Well, we have no extraced separators, so we
     237             :                              * won`t check them for consistency. This will allow us to
     238             :                              * work with "C" locale from other locales
     239             :                              */
     240           0 :                             return main_convert_loop();
     241             :                         } else {
     242           0 :                             if (m_begin == m_end) return false;
     243           0 :                             if (current_grouping < grouping_size - 1) ++current_grouping;
     244           0 :                             remained = grouping[current_grouping];
     245             :                         }
     246             :                     }
     247           0 :                 } /*for*/
     248             : 
     249           0 :                 return true;
     250             : #endif
     251       15000 :             }
     252             : 
     253             :         private:
     254             :             // Iteration that does not care about grouping/separators and assumes that all
     255             :             // input characters are digits
     256             : #if defined(__clang__) && (__clang_major__ > 3 || __clang_minor__ > 6)
     257             :             __attribute__((no_sanitize("unsigned-integer-overflow")))
     258             : #endif
     259       20000 :             inline bool main_convert_iteration() noexcept {
     260       20000 :                 CharT const czero = lcast_char_constants<CharT>::zero;
     261       20000 :                 T const maxv = (std::numeric_limits<T>::max)();
     262             : 
     263       20000 :                 m_multiplier_overflowed = m_multiplier_overflowed || (maxv/10 < m_multiplier);
     264       20000 :                 m_multiplier = static_cast<T>(m_multiplier * 10);
     265             : 
     266       20000 :                 T const dig_value = static_cast<T>(*m_end - czero);
     267       20000 :                 T const new_sub_value = static_cast<T>(m_multiplier * dig_value);
     268             : 
     269             :                 // We must correctly handle situations like `000000000000000000000000000001`.
     270             :                 // So we take care of overflow only if `dig_value` is not '0'.
     271       34763 :                 if (*m_end < czero || *m_end >= czero + 10  // checking for correct digit
     272       20000 :                     || (dig_value && (                      // checking for overflow of ...
     273       14763 :                         m_multiplier_overflowed                             // ... multiplier
     274       14763 :                         || static_cast<T>(maxv / dig_value) < m_multiplier  // ... subvalue
     275       14763 :                         || static_cast<T>(maxv - new_sub_value) < m_value   // ... whole expression
     276             :                     ))
     277           0 :                 ) return false;
     278             : 
     279       20000 :                 m_value = static_cast<T>(m_value + new_sub_value);
     280             : 
     281       20000 :                 return true;
     282       20000 :             }
     283             : 
     284       15000 :             bool main_convert_loop() noexcept {
     285       35000 :                 for ( ; m_end >= m_begin; --m_end) {
     286       20000 :                     if (!main_convert_iteration()) {
     287           0 :                         return false;
     288             :                     }
     289       20000 :                 }
     290             : 
     291       15000 :                 return true;
     292       15000 :             }
     293             :         };
     294             :     }
     295             : } // namespace boost
     296             : 
     297             : #endif // BOOST_LEXICAL_CAST_DETAIL_LCAST_UNSIGNED_CONVERTERS_HPP
     298             : 

Generated by: LCOV version 1.16