aboutsummaryrefslogtreecommitdiff
path: root/src/util/strencodings.cpp
diff options
context:
space:
mode:
authorW. J. van der Laan <laanwj@protonmail.com>2021-09-30 14:47:03 +0200
committerW. J. van der Laan <laanwj@protonmail.com>2021-09-30 15:14:58 +0200
commit2d8e0c0c3c0d3c4cee7bb52d1edf501f40c53463 (patch)
tree8266dac1d118b5985f5f84b1550752198bc21f38 /src/util/strencodings.cpp
parent1cf7fb9fd65820b298a0eb28f6e2c4c7409c78d1 (diff)
parent4747db876154ddd828c03d9eda10ecf8b25d8dc8 (diff)
downloadbitcoin-2d8e0c0c3c0d3c4cee7bb52d1edf501f40c53463.tar.xz
Merge bitcoin/bitcoin#20457: util: Make Parse{Int,UInt}{32,64} use locale independent std::from_chars(…) (C++17) instead of locale dependent strto{l,ll,ul,ull}
4747db876154ddd828c03d9eda10ecf8b25d8dc8 util: Introduce ToIntegral<T>(const std::string&) for locale independent parsing using std::from_chars(…) (C++17) (practicalswift) Pull request description: Make `Parse{Int,UInt}{32,64}` use locale independent `std::from_chars(…)` (C++17) instead of locale dependent `strto{l,ll,ul,ull}`. [About `std::from_chars`](https://en.cppreference.com/w/cpp/utility/from_chars): _"Unlike other parsing functions in C++ and C libraries, `std::from_chars` is locale-independent, non-allocating, and non-throwing."_ ACKs for top commit: laanwj: Code review ACK 4747db876154ddd828c03d9eda10ecf8b25d8dc8 Tree-SHA512: 40f2cd582bc19ddcf2c498eca3379167619eff6aa047bbac2f73b8fd8ecaefe5947c66700a189f83848751f9f8c05645e83afd4a44a1679062aee5440dba880a
Diffstat (limited to 'src/util/strencodings.cpp')
-rw-r--r--src/util/strencodings.cpp110
1 files changed, 38 insertions, 72 deletions
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index f514613f0d..0aa80ea0ae 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -11,8 +11,7 @@
#include <algorithm>
#include <cstdlib>
#include <cstring>
-#include <errno.h>
-#include <limits>
+#include <optional>
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@@ -282,6 +281,32 @@ std::string DecodeBase32(const std::string& str, bool* pf_invalid)
return std::string((const char*)vchRet.data(), vchRet.size());
}
+[[nodiscard]] static bool ParsePrechecks(const std::string&);
+
+namespace {
+template <typename T>
+bool ParseIntegral(const std::string& str, T* out)
+{
+ static_assert(std::is_integral<T>::value);
+ if (!ParsePrechecks(str)) {
+ return false;
+ }
+ // Replicate the exact behavior of strtol/strtoll/strtoul/strtoull when
+ // handling leading +/- for backwards compatibility.
+ if (str.length() >= 2 && str[0] == '+' && str[1] == '-') {
+ return false;
+ }
+ const std::optional<T> opt_int = ToIntegral<T>((!str.empty() && str[0] == '+') ? str.substr(1) : str);
+ if (!opt_int) {
+ return false;
+ }
+ if (out != nullptr) {
+ *out = *opt_int;
+ }
+ return true;
+}
+}; // namespace
+
[[nodiscard]] static bool ParsePrechecks(const std::string& str)
{
if (str.empty()) // No empty string allowed
@@ -293,95 +318,36 @@ std::string DecodeBase32(const std::string& str, bool* pf_invalid)
return true;
}
-bool ParseInt32(const std::string& str, int32_t *out)
+bool ParseInt32(const std::string& str, int32_t* out)
{
- if (!ParsePrechecks(str))
- return false;
- char *endp = nullptr;
- errno = 0; // strtol will not set errno if valid
- long int n = strtol(str.c_str(), &endp, 10);
- if(out) *out = (int32_t)n;
- // Note that strtol returns a *long int*, so even if strtol doesn't report an over/underflow
- // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
- // platforms the size of these types may be different.
- return endp && *endp == 0 && !errno &&
- n >= std::numeric_limits<int32_t>::min() &&
- n <= std::numeric_limits<int32_t>::max();
+ return ParseIntegral<int32_t>(str, out);
}
-bool ParseInt64(const std::string& str, int64_t *out)
+bool ParseInt64(const std::string& str, int64_t* out)
{
- if (!ParsePrechecks(str))
- return false;
- char *endp = nullptr;
- errno = 0; // strtoll will not set errno if valid
- long long int n = strtoll(str.c_str(), &endp, 10);
- if(out) *out = (int64_t)n;
- // Note that strtoll returns a *long long int*, so even if strtol doesn't report an over/underflow
- // we still have to check that the returned value is within the range of an *int64_t*.
- return endp && *endp == 0 && !errno &&
- n >= std::numeric_limits<int64_t>::min() &&
- n <= std::numeric_limits<int64_t>::max();
+ return ParseIntegral<int64_t>(str, out);
}
-bool ParseUInt8(const std::string& str, uint8_t *out)
+bool ParseUInt8(const std::string& str, uint8_t* out)
{
- uint32_t u32;
- if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint8_t>::max()) {
- return false;
- }
- if (out != nullptr) {
- *out = static_cast<uint8_t>(u32);
- }
- return true;
+ return ParseIntegral<uint8_t>(str, out);
}
bool ParseUInt16(const std::string& str, uint16_t* out)
{
- uint32_t u32;
- if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint16_t>::max()) {
- return false;
- }
- if (out != nullptr) {
- *out = static_cast<uint16_t>(u32);
- }
- return true;
+ return ParseIntegral<uint16_t>(str, out);
}
-bool ParseUInt32(const std::string& str, uint32_t *out)
+bool ParseUInt32(const std::string& str, uint32_t* out)
{
- if (!ParsePrechecks(str))
- return false;
- if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoul accepts these by default if they fit in the range
- return false;
- char *endp = nullptr;
- errno = 0; // strtoul will not set errno if valid
- unsigned long int n = strtoul(str.c_str(), &endp, 10);
- if(out) *out = (uint32_t)n;
- // Note that strtoul returns a *unsigned long int*, so even if it doesn't report an over/underflow
- // we still have to check that the returned value is within the range of an *uint32_t*. On 64-bit
- // platforms the size of these types may be different.
- return endp && *endp == 0 && !errno &&
- n <= std::numeric_limits<uint32_t>::max();
+ return ParseIntegral<uint32_t>(str, out);
}
-bool ParseUInt64(const std::string& str, uint64_t *out)
+bool ParseUInt64(const std::string& str, uint64_t* out)
{
- if (!ParsePrechecks(str))
- return false;
- if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoull accepts these by default if they fit in the range
- return false;
- char *endp = nullptr;
- errno = 0; // strtoull will not set errno if valid
- unsigned long long int n = strtoull(str.c_str(), &endp, 10);
- if(out) *out = (uint64_t)n;
- // Note that strtoull returns a *unsigned long long int*, so even if it doesn't report an over/underflow
- // we still have to check that the returned value is within the range of an *uint64_t*.
- return endp && *endp == 0 && !errno &&
- n <= std::numeric_limits<uint64_t>::max();
+ return ParseIntegral<uint64_t>(str, out);
}
-
bool ParseDouble(const std::string& str, double *out)
{
if (!ParsePrechecks(str))