2020#ifndef BEAST_MODULE_CORE_TEXT_LEXICALCAST_H_INCLUDED
2121#define BEAST_MODULE_CORE_TEXT_LEXICALCAST_H_INCLUDED
2222
23+ #include < ripple/beast/type_name.h>
24+
25+ #include < boost/beast/core/string_type.hpp>
26+ #include < boost/utility/string_view.hpp>
27+
2328#include < algorithm>
24- #include < cassert>
25- #include < cerrno>
26- #include < cstdlib>
27- #include < iostream>
28- #include < iterator>
29+ #include < array>
30+ #include < cctype>
31+ #include < charconv>
2932#include < limits>
3033#include < string>
34+ #include < string_view>
35+ #include < system_error>
3136#include < type_traits>
32- #include < typeinfo>
33- #include < utility>
34-
35- #include < boost/predef.h>
3637
3738namespace beast {
3839
39- namespace detail {
40-
41- #if BOOST_COMP_MSVC
42- #pragma warning(push)
43- #pragma warning(disable : 4800)
44- #pragma warning(disable : 4804)
45- #endif
40+ // ------------------------------------------------------------------------------
4641
47- template <class Int , class FwdIt , class Accumulator >
48- bool
49- parse_integral (Int& num, FwdIt first, FwdIt last, Accumulator accumulator)
50- {
51- num = 0 ;
42+ namespace detail {
5243
53- if (first == last)
44+ template <class T >
45+ inline constexpr bool is_boost_string_view_v = []() {
46+ if constexpr (std::is_same_v<T, std::string_view>)
5447 return false ;
48+ else if constexpr (std::is_same_v<T, boost::core::string_view>)
49+ return true ;
50+ else
51+ return std::is_same_v<T, boost::beast::string_view>;
52+ }();
5553
56- while (first != last)
57- {
58- auto const c = *first++;
59- if (c < ' 0' || c > ' 9' )
60- return false ;
61- if (!accumulator (num, Int (c - ' 0' )))
62- return false ;
63- }
64-
65- return true ;
66- }
67-
68- template <class Int , class FwdIt >
69- bool
70- parse_negative_integral (Int& num, FwdIt first, FwdIt last)
71- {
72- Int limit_value = std::numeric_limits<Int>::min () / 10 ;
73- Int limit_digit = std::numeric_limits<Int>::min () % 10 ;
74-
75- if (limit_digit < 0 )
76- limit_digit = -limit_digit;
77-
78- return parse_integral<Int>(
79- num, first, last, [limit_value, limit_digit](Int& value, Int digit) {
80- assert ((digit >= 0 ) && (digit <= 9 ));
81- if (value < limit_value ||
82- (value == limit_value && digit > limit_digit))
83- return false ;
84- value = (value * 10 ) - digit;
85- return true ;
86- });
87- }
88-
89- template <class Int , class FwdIt >
90- bool
91- parse_positive_integral (Int& num, FwdIt first, FwdIt last)
92- {
93- Int limit_value = std::numeric_limits<Int>::max () / 10 ;
94- Int limit_digit = std::numeric_limits<Int>::max () % 10 ;
95-
96- return parse_integral<Int>(
97- num, first, last, [limit_value, limit_digit](Int& value, Int digit) {
98- assert ((digit >= 0 ) && (digit <= 9 ));
99- if (value > limit_value ||
100- (value == limit_value && digit > limit_digit))
101- return false ;
102- value = (value * 10 ) + digit;
103- return true ;
104- });
105- }
106-
107- template <class IntType , class FwdIt >
108- bool
109- parseSigned (IntType& result, FwdIt first, FwdIt last)
110- {
111- static_assert (
112- std::is_signed<IntType>::value,
113- " You may only call parseSigned with a signed integral type." );
114-
115- if (first != last && *first == ' -' )
116- return parse_negative_integral (result, first + 1 , last);
117-
118- if (first != last && *first == ' +' )
119- return parse_positive_integral (result, first + 1 , last);
120-
121- return parse_positive_integral (result, first, last);
122- }
123-
124- template <class UIntType , class FwdIt >
125- bool
126- parseUnsigned (UIntType& result, FwdIt first, FwdIt last)
127- {
128- static_assert (
129- std::is_unsigned<UIntType>::value,
130- " You may only call parseUnsigned with an unsigned integral type." );
131-
132- if (first != last && *first == ' +' )
133- return parse_positive_integral (result, first + 1 , last);
134-
135- return parse_positive_integral (result, first, last);
136- }
54+ } // namespace detail
13755
13856// ------------------------------------------------------------------------------
13957
140- // These specializatons get called by the non-member functions to do the work
141- template <class Out , class In >
142- struct LexicalCast ;
143-
144- // conversion to std::string
145- template <class In >
146- struct LexicalCast <std::string, In>
58+ /* * Thrown when a conversion is not possible with LexicalCast.
59+ Only used in the throw variants of lexicalCast.
60+ */
61+ struct BadLexicalCast : public std ::bad_cast
14762{
148- explicit LexicalCast () = default;
63+ private:
64+ std::string msg;
14965
150- template < class Arithmetic = In>
151- std::enable_if_t <std::is_arithmetic<Arithmetic>::value, bool >
152- operator ()( std::string& out, Arithmetic in )
66+ public:
67+ explicit BadLexicalCast ( std::string m = {})
68+ : msg( std::bad_cast::what() )
15369 {
154- out = std::to_string (in);
155- return true ;
70+ if (!m. empty ())
71+ msg += " : " + m ;
15672 }
15773
158- template <class Enumeration = In>
159- std::enable_if_t <std::is_enum<Enumeration>::value, bool >
160- operator ()(std::string& out, Enumeration in)
74+ [[nodiscard]] char const *
75+ what () const noexcept override
16176 {
162- out = std::to_string (
163- static_cast <std::underlying_type_t <Enumeration>>(in));
164- return true ;
77+ return msg.c_str ();
16578 }
16679};
16780
168- // Parse std::string to number
81+ // ------------------------------------------------------------------------------
82+
83+ /* * Convert from std::string_view to integral type.
84+ @return `false` if there was a parsing or range error
85+ */
16986template <class Out >
170- struct LexicalCast <Out, std::string>
87+ requires std::is_integral_v<Out> && (!std::is_same_v<Out, bool >)
88+ [[nodiscard]] bool
89+ lexicalCastChecked (Out& out, std::string_view in) noexcept
17190{
172- explicit LexicalCast () = default;
173-
174- static_assert (
175- std::is_integral<Out>::value,
176- " beast::LexicalCast can only be used with integral types" );
91+ if (in.empty ())
92+ return false ;
17793
178- template <class Integral = Out>
179- std::enable_if_t <std::is_unsigned<Integral>::value, bool >
180- operator ()(Integral& out, std::string const & in) const
94+ if (in.front () == ' +' )
18195 {
182- return parseUnsigned (out, in.begin (), in.end ());
183- }
96+ in.remove_prefix (1 );
18497
185- template <class Integral = Out>
186- std::enable_if_t <std::is_signed<Integral>::value, bool >
187- operator ()(Integral& out, std::string const & in) const
188- {
189- return parseSigned (out, in.begin (), in.end ());
98+ if (in.empty () || in.front () == ' -' )
99+ return false ;
190100 }
191101
192- bool
193- operator ()(bool & out, std::string in) const
194- {
195- // Convert the input to lowercase
196- std::transform (in.begin (), in.end (), in.begin (), [](auto c) {
197- return std::tolower (static_cast <unsigned char >(c));
198- });
199-
200- if (in == " 1" || in == " true" )
201- {
202- out = true ;
203- return true ;
204- }
205-
206- if (in == " 0" || in == " false" )
207- {
208- out = false ;
209- return true ;
210- }
211-
212- return false ;
213- }
214- };
102+ auto [ptr, ec] = std::from_chars (in.data (), in.data () + in.size (), out);
215103
216- // ------------------------------------------------------------------------------
104+ return ec == std::errc{} && ptr == in.data () + in.size ();
105+ }
217106
218- // Conversion from null terminated char const*
219- template <class Out >
220- struct LexicalCast <Out, char const *>
107+ /* * Convert from std::string_view to bool.
108+ @return `false` if there was a parsing error
109+ */
110+ [[nodiscard]] inline bool
111+ lexicalCastChecked (bool & out, std::string_view in) noexcept
221112{
222- explicit LexicalCast () = default;
223-
224- bool
225- operator ()(Out& out, char const * in) const
113+ auto iequals = [](std::string_view a, std::string_view b) {
114+ return std::equal (
115+ a.begin (), a.end (), b.begin (), b.end (), [](char ca, char cb) {
116+ return std::tolower (static_cast <unsigned char >(ca)) ==
117+ std::tolower (static_cast <unsigned char >(cb));
118+ });
119+ };
120+
121+ if (in == " 1" || iequals (in, " true" ))
226122 {
227- return LexicalCast<Out, std::string>()(out, in);
123+ out = true ;
124+ return true ;
228125 }
229- };
230126
231- // Conversion from null terminated char*
232- // The string is not modified.
233- template <class Out >
234- struct LexicalCast <Out, char *>
235- {
236- explicit LexicalCast () = default;
237-
238- bool
239- operator ()(Out& out, char * in) const
127+ if (in == " 0" || iequals (in, " false" ))
240128 {
241- return LexicalCast<Out, std::string>()(out, in);
129+ out = false ;
130+ return true ;
242131 }
243- };
244132
245- #if BOOST_COMP_MSVC
246- #pragma warning(pop)
247- #endif
133+ return false ;
134+ }
248135
249- } // namespace detail
136+ /* * Convert from integral type to std::string.
137+ @return `false` if there was a conversion error
138+ */
139+ template <class In >
140+ requires std::is_integral_v<In>
141+ [[nodiscard]] bool
142+ lexicalCastChecked (std::string& out, In in) noexcept
143+ {
144+ std::array<char , std::numeric_limits<In>::digits10 + 3 > buf;
250145
251- // ------------------------------------------------------------------------------
146+ auto [ptr, ec] = std::to_chars (buf. data (), buf. data () + buf. size (), in);
252147
253- /* * Thrown when a conversion is not possible with LexicalCast.
254- Only used in the throw variants of lexicalCast.
148+ if (ec != std::errc{})
149+ return false ;
150+
151+ out.assign (buf.data (), ptr);
152+ return true ;
153+ }
154+
155+ /* * Convert from enum type to std::string.
156+ @return `false` if there was a conversion error
255157*/
256- struct BadLexicalCast : public std ::bad_cast
158+ template <class In >
159+ requires std::is_enum_v<In>
160+ [[nodiscard]] bool
161+ lexicalCastChecked (std::string& out, In in) noexcept
257162{
258- explicit BadLexicalCast () = default ;
259- };
163+ return lexicalCastChecked (out, static_cast <std:: underlying_type_t <In>>(in)) ;
164+ }
260165
261- /* * Intelligently convert from one type to another .
166+ /* * Convert from Boost string_view types to integral types .
262167 @return `false` if there was a parsing or range error
263168*/
264169template <class Out , class In >
265- bool
266- lexicalCastChecked (Out& out, In in)
170+ requires detail::is_boost_string_view_v<In>
171+ [[nodiscard]] bool
172+ lexicalCastChecked (Out& out, In in) noexcept
267173{
268- return detail::LexicalCast<Out, In>()(out , in);
174+ return lexicalCastChecked (out, std::string_view (in. data () , in. size ()) );
269175}
270176
271- /* * Convert from one type to another, throw on error
177+ // ------------------------------------------------------------------------------
178+
179+ /* * Convert from one type to another, throw on error.
272180
273181 An exception of type BadLexicalCast is thrown if the conversion fails.
274182
@@ -278,26 +186,27 @@ template <class Out, class In>
278186Out
279187lexicalCastThrow (In in)
280188{
281- Out out;
282-
283- if (lexicalCastChecked (out, in))
189+ if (Out out; lexicalCastChecked (out, in))
284190 return out;
285191
286- throw BadLexicalCast ();
192+ throw BadLexicalCast (
193+ #ifdef DEBUG
194+ beast::type_name<In>() + " -> " + beast::type_name<Out>()
195+ #endif
196+ );
287197}
288198
289199/* * Convert from one type to another.
290200
291- @param defaultValue The value returned if parsing fails
201+ @param in The value to convert.
202+ @param defaultValue The value returned if parsing fails.
292203 @return The new type.
293204*/
294205template <class Out , class In >
295- Out
206+ [[nodiscard]] Out
296207lexicalCast (In in, Out defaultValue = Out())
297208{
298- Out out;
299-
300- if (lexicalCastChecked (out, in))
209+ if (Out out; lexicalCastChecked (out, in))
301210 return out;
302211
303212 return defaultValue;
0 commit comments