mirror of
https://github.com/Tencent/flare.git
synced 2026-10-17 04:41:22 +08:00
fix(string): revert errno in TryParseImpl and use std::from_chars instead of strtoxxx (#128)
This commit is contained in:
+12
-27
@@ -15,6 +15,7 @@
|
||||
#include "flare/base/string.h"
|
||||
|
||||
#include <array>
|
||||
#include <charconv>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
@@ -55,6 +56,13 @@ std::optional<T> TryParseImpl(F fptr, const char* s,
|
||||
if (FLARE_UNLIKELY(s[0] == 0)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
struct LastErrorStasher {
|
||||
LastErrorStasher() : last_errno(errno) { errno = 0; }
|
||||
~LastErrorStasher() { errno = last_errno; }
|
||||
int last_errno;
|
||||
} _;
|
||||
|
||||
char* end;
|
||||
auto result = fptr(s, &end, args...);
|
||||
if (end != s + strlen(s)) { // Return value `0` is also handled by this test.
|
||||
@@ -241,9 +249,6 @@ bool IEquals(std::string_view first, std::string_view second) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(luobogao): Once we have `from_chars` at hand, use it instead (so as to
|
||||
// support `std::string_view` efficiently).
|
||||
|
||||
std::optional<bool> TryParseTraits<bool>::TryParse(
|
||||
std::string_view s, bool recognizes_alphabet_symbol, bool ignore_case) {
|
||||
if (auto num_opt = flare::TryParse<int>(s); num_opt) {
|
||||
@@ -266,32 +271,12 @@ template <class T>
|
||||
std::optional<T>
|
||||
TryParseTraits<T, std::enable_if_t<std::is_integral_v<T>>>::TryParse(
|
||||
std::string_view s, int base) {
|
||||
// `strtoll` expects a terminating null, therefore we copy `s` into this
|
||||
// temporary buffer before calling that method.
|
||||
//
|
||||
// Comparing to constructing a `std::string` here, this saves us a memory
|
||||
// allocation.
|
||||
char temp_buffer[129];
|
||||
if (FLARE_UNLIKELY(s.size() > 128)) { // Out-of-range anyway.
|
||||
T value;
|
||||
auto [ptr, ec] = std::from_chars(s.begin(), s.end(), value, base);
|
||||
if (ec != std::errc() || ptr != s.end()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
memcpy(temp_buffer, s.data(), s.size());
|
||||
temp_buffer[s.size()] = 0;
|
||||
|
||||
// Here we always use the largest type to hold the result, and check if the
|
||||
// result is actually larger than what `T` can hold.
|
||||
//
|
||||
// It's not very efficient, though.
|
||||
if constexpr (std::is_signed_v<T>) {
|
||||
auto opt = TryParseImpl<std::int64_t>(&strtoll, temp_buffer,
|
||||
{LLONG_MIN, LLONG_MAX}, base);
|
||||
return TryNarrowCast<T>(opt);
|
||||
} else {
|
||||
auto opt =
|
||||
TryParseImpl<std::uint64_t>(&strtoull, temp_buffer, {ULLONG_MAX}, base);
|
||||
return TryNarrowCast<T>(opt);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
||||
@@ -28,6 +28,7 @@ TEST(String, TryParseIntegral) {
|
||||
ASSERT_FALSE(TryParse<int>(std::string(123456, 'a')));
|
||||
ASSERT_FALSE(TryParse<int>(""));
|
||||
ASSERT_FALSE(TryParse<int>("a"));
|
||||
ASSERT_FALSE(TryParse<int>("1-2"));
|
||||
ASSERT_FALSE(TryParse<std::int8_t>(std::to_string(INT8_MAX + 1LL)));
|
||||
ASSERT_FALSE(TryParse<std::int8_t>(std::to_string(INT8_MIN - 1LL)));
|
||||
ASSERT_FALSE(TryParse<std::uint8_t>(std::to_string(UINT8_MAX + 1LL)));
|
||||
@@ -44,7 +45,9 @@ TEST(String, TryParseIntegral) {
|
||||
ASSERT_FALSE(TryParse<std::int64_t>(std::to_string(INT64_MIN) + "0"));
|
||||
ASSERT_FALSE(TryParse<std::uint64_t>(std::to_string(UINT64_MAX) + "0"));
|
||||
ASSERT_FALSE(TryParse<std::uint64_t>("-1"));
|
||||
|
||||
ASSERT_TRUE(TryParse<int>("0"));
|
||||
ASSERT_TRUE(TryParse<std::int64_t>(std::to_string(INT64_MAX)));
|
||||
ASSERT_TRUE(TryParse<std::int8_t>("0"));
|
||||
ASSERT_TRUE(TryParse<std::uint8_t>("0"));
|
||||
ASSERT_TRUE(TryParse<std::int16_t>("0"));
|
||||
@@ -79,12 +82,13 @@ TEST(String, TryParseFloatingPoint) {
|
||||
ASSERT_FALSE(TryParse<long double>(""));
|
||||
ASSERT_FALSE(TryParse<double>("a"));
|
||||
ASSERT_FALSE(TryParse<long double>("a"));
|
||||
ASSERT_FALSE(TryParse<float>(std::to_string(HUGE_VALF * 10)));
|
||||
ASSERT_FALSE(TryParse<float>(std::to_string(-HUGE_VALF * 10)));
|
||||
ASSERT_FALSE(TryParse<double>(std::to_string(HUGE_VAL * 10)));
|
||||
ASSERT_FALSE(TryParse<double>(std::to_string(-HUGE_VAL * 10)));
|
||||
ASSERT_FALSE(TryParse<long double>(std::to_string(HUGE_VALL * 10)));
|
||||
ASSERT_FALSE(TryParse<long double>(std::to_string(-HUGE_VALL * 10)));
|
||||
|
||||
ASSERT_TRUE(TryParse<float>(std::to_string(HUGE_VALF)));
|
||||
ASSERT_TRUE(TryParse<float>(std::to_string(-HUGE_VALF)));
|
||||
ASSERT_TRUE(TryParse<double>(std::to_string(HUGE_VAL)));
|
||||
ASSERT_TRUE(TryParse<double>(std::to_string(-HUGE_VAL)));
|
||||
ASSERT_TRUE(TryParse<long double>(std::to_string(HUGE_VALL)));
|
||||
ASSERT_TRUE(TryParse<long double>(std::to_string(-HUGE_VALL)));
|
||||
|
||||
for (int i = 0; i != 100000; ++i) {
|
||||
auto x = 1.0 * Random() * Random();
|
||||
|
||||
Reference in New Issue
Block a user