refactor: replace hand-rolled lexicalCast (#4473)

Replace hand-rolled code with std::from_chars for better
maintainability.

The C++ std::from_chars function is intended to be as fast as possible,
so it is unlikely to be slower than the code it replaces. This change is
a net gain because it reduces the amount of hand-rolled code.
This commit is contained in:
Denis Angell
2023-06-27 02:50:03 -04:00
committed by tequ
parent 0f0ffda053
commit 5d2d1d4497
2 changed files with 22 additions and 131 deletions

View File

@@ -522,7 +522,7 @@ getHashByIndex(soci::session& session, LedgerIndex ledgerIndex)
std::string sql = std::string sql =
"SELECT LedgerHash FROM Ledgers INDEXED BY SeqLedger WHERE LedgerSeq='"; "SELECT LedgerHash FROM Ledgers INDEXED BY SeqLedger WHERE LedgerSeq='";
sql.append(beast::lexicalCastThrow<std::string>(ledgerIndex)); sql.append(std::to_string(ledgerIndex));
sql.append("';"); sql.append("';");
std::string hash; std::string hash;
@@ -585,9 +585,9 @@ getHashesByIndex(
{ {
std::string sql = std::string sql =
"SELECT LedgerSeq,LedgerHash,PrevHash FROM Ledgers WHERE LedgerSeq >= "; "SELECT LedgerSeq,LedgerHash,PrevHash FROM Ledgers WHERE LedgerSeq >= ";
sql.append(beast::lexicalCastThrow<std::string>(minSeq)); sql.append(std::to_string(minSeq));
sql.append(" AND LedgerSeq <= "); sql.append(" AND LedgerSeq <= ");
sql.append(beast::lexicalCastThrow<std::string>(maxSeq)); sql.append(std::to_string(maxSeq));
sql.append(";"); sql.append(";");
std::uint64_t ls; std::uint64_t ls;
@@ -765,8 +765,7 @@ transactionsSQL(
boost::format("SELECT %s FROM AccountTransactions " boost::format("SELECT %s FROM AccountTransactions "
"WHERE Account = '%s' %s %s LIMIT %u, %u;") % "WHERE Account = '%s' %s %s LIMIT %u, %u;") %
selection % toBase58(options.account) % maxClause % minClause % selection % toBase58(options.account) % maxClause % minClause %
beast::lexicalCastThrow<std::string>(options.offset) % options.offset % numberOfResults);
beast::lexicalCastThrow<std::string>(numberOfResults));
else else
sql = boost::str( sql = boost::str(
boost::format( boost::format(
@@ -779,9 +778,7 @@ transactionsSQL(
"LIMIT %u, %u;") % "LIMIT %u, %u;") %
selection % toBase58(options.account) % maxClause % minClause % selection % toBase58(options.account) % maxClause % minClause %
(descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") %
(descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") % options.offset % numberOfResults);
beast::lexicalCastThrow<std::string>(options.offset) %
beast::lexicalCastThrow<std::string>(numberOfResults));
JLOG(j.trace()) << "txSQL query: " << sql; JLOG(j.trace()) << "txSQL query: " << sql;
return sql; return sql;
} }

View File

@@ -23,8 +23,8 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cerrno> #include <cerrno>
#include <charconv>
#include <cstdlib> #include <cstdlib>
#include <iostream>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <string> #include <string>
@@ -32,111 +32,10 @@
#include <typeinfo> #include <typeinfo>
#include <utility> #include <utility>
#include <boost/predef.h>
namespace beast { namespace beast {
namespace detail { namespace detail {
#if BOOST_COMP_MSVC
#pragma warning(push)
#pragma warning(disable : 4800)
#pragma warning(disable : 4804)
#endif
template <class Int, class FwdIt, class Accumulator>
bool
parse_integral(Int& num, FwdIt first, FwdIt last, Accumulator accumulator)
{
num = 0;
if (first == last)
return false;
while (first != last)
{
auto const c = *first++;
if (c < '0' || c > '9')
return false;
if (!accumulator(num, Int(c - '0')))
return false;
}
return true;
}
template <class Int, class FwdIt>
bool
parse_negative_integral(Int& num, FwdIt first, FwdIt last)
{
Int limit_value = std::numeric_limits<Int>::min() / 10;
Int limit_digit = std::numeric_limits<Int>::min() % 10;
if (limit_digit < 0)
limit_digit = -limit_digit;
return parse_integral<Int>(
num, first, last, [limit_value, limit_digit](Int& value, Int digit) {
assert((digit >= 0) && (digit <= 9));
if (value < limit_value ||
(value == limit_value && digit > limit_digit))
return false;
value = (value * 10) - digit;
return true;
});
}
template <class Int, class FwdIt>
bool
parse_positive_integral(Int& num, FwdIt first, FwdIt last)
{
Int limit_value = std::numeric_limits<Int>::max() / 10;
Int limit_digit = std::numeric_limits<Int>::max() % 10;
return parse_integral<Int>(
num, first, last, [limit_value, limit_digit](Int& value, Int digit) {
assert((digit >= 0) && (digit <= 9));
if (value > limit_value ||
(value == limit_value && digit > limit_digit))
return false;
value = (value * 10) + digit;
return true;
});
}
template <class IntType, class FwdIt>
bool
parseSigned(IntType& result, FwdIt first, FwdIt last)
{
static_assert(
std::is_signed<IntType>::value,
"You may only call parseSigned with a signed integral type.");
if (first != last && *first == '-')
return parse_negative_integral(result, first + 1, last);
if (first != last && *first == '+')
return parse_positive_integral(result, first + 1, last);
return parse_positive_integral(result, first, last);
}
template <class UIntType, class FwdIt>
bool
parseUnsigned(UIntType& result, FwdIt first, FwdIt last)
{
static_assert(
std::is_unsigned<UIntType>::value,
"You may only call parseUnsigned with an unsigned integral type.");
if (first != last && *first == '+')
return parse_positive_integral(result, first + 1, last);
return parse_positive_integral(result, first, last);
}
//------------------------------------------------------------------------------
// These specializatons get called by the non-member functions to do the work // These specializatons get called by the non-member functions to do the work
template <class Out, class In> template <class Out, class In>
struct LexicalCast; struct LexicalCast;
@@ -148,7 +47,7 @@ struct LexicalCast<std::string, In>
explicit LexicalCast() = default; explicit LexicalCast() = default;
template <class Arithmetic = In> template <class Arithmetic = In>
std::enable_if_t<std::is_arithmetic<Arithmetic>::value, bool> std::enable_if_t<std::is_arithmetic_v<Arithmetic>, bool>
operator()(std::string& out, Arithmetic in) operator()(std::string& out, Arithmetic in)
{ {
out = std::to_string(in); out = std::to_string(in);
@@ -156,7 +55,7 @@ struct LexicalCast<std::string, In>
} }
template <class Enumeration = In> template <class Enumeration = In>
std::enable_if_t<std::is_enum<Enumeration>::value, bool> std::enable_if_t<std::is_enum_v<Enumeration>, bool>
operator()(std::string& out, Enumeration in) operator()(std::string& out, Enumeration in)
{ {
out = std::to_string( out = std::to_string(
@@ -172,21 +71,24 @@ struct LexicalCast<Out, std::string>
explicit LexicalCast() = default; explicit LexicalCast() = default;
static_assert( static_assert(
std::is_integral<Out>::value, std::is_integral_v<Out>,
"beast::LexicalCast can only be used with integral types"); "beast::LexicalCast can only be used with integral types");
template <class Integral = Out> template <class Integral = Out>
std::enable_if_t<std::is_unsigned<Integral>::value, bool> std::enable_if_t<
std::is_integral_v<Integral> && !std::is_same_v<Integral, bool>,
bool>
operator()(Integral& out, std::string const& in) const operator()(Integral& out, std::string const& in) const
{ {
return parseUnsigned(out, in.begin(), in.end()); auto first = in.data();
} auto last = in.data() + in.size();
template <class Integral = Out> if (first != last && *first == '+')
std::enable_if_t<std::is_signed<Integral>::value, bool> ++first;
operator()(Integral& out, std::string const& in) const
{ auto ret = std::from_chars(first, last, out);
return parseSigned(out, in.begin(), in.end());
return ret.ec == std::errc() && ret.ptr == last;
} }
bool bool
@@ -242,10 +144,6 @@ struct LexicalCast<Out, char*>
} }
}; };
#if BOOST_COMP_MSVC
#pragma warning(pop)
#endif
} // namespace detail } // namespace detail
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -278,9 +176,7 @@ template <class Out, class In>
Out Out
lexicalCastThrow(In in) lexicalCastThrow(In in)
{ {
Out out; if (Out out; lexicalCastChecked(out, in))
if (lexicalCastChecked(out, in))
return out; return out;
throw BadLexicalCast(); throw BadLexicalCast();
@@ -295,9 +191,7 @@ template <class Out, class In>
Out Out
lexicalCast(In in, Out defaultValue = Out()) lexicalCast(In in, Out defaultValue = Out())
{ {
Out out; if (Out out; lexicalCastChecked(out, in))
if (lexicalCastChecked(out, in))
return out; return out;
return defaultValue; return defaultValue;