This is an automated email from the ASF dual-hosted git repository.
hulk pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new bf66a9e24 chore(util): replace strtof/strtod with fast-float (#3394)
bf66a9e24 is described below
commit bf66a9e24ba3dd4b5740721d89526e3cd687f224
Author: Twice <[email protected]>
AuthorDate: Fri Mar 20 09:59:03 2026 +0800
chore(util): replace strtof/strtod with fast-float (#3394)
It closes #3344.
---
CMakeLists.txt | 2 ++
NOTICE | 4 ++++
cmake/fast_float.cmake | 31 ++++++++++++++++++++++++++++
src/common/parse_util.h | 49 ++++++++++++++-------------------------------
tests/cppunit/parse_util.cc | 13 ++++++++----
5 files changed, 61 insertions(+), 38 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 11beb3a28..a6c5343dd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -185,6 +185,7 @@ include(cmake/tbb.cmake)
include(cmake/rocksdb.cmake)
include(cmake/libevent.cmake)
include(cmake/fmt.cmake)
+include(cmake/fast_float.cmake)
include(cmake/spdlog.cmake)
include(cmake/jsoncons.cmake)
include(cmake/xxhash.cmake)
@@ -209,6 +210,7 @@ list(APPEND EXTERNAL_LIBS lz4)
list(APPEND EXTERNAL_LIBS zstd)
list(APPEND EXTERNAL_LIBS zlib_with_headers)
list(APPEND EXTERNAL_LIBS fmt)
+list(APPEND EXTERNAL_LIBS fast_float)
list(APPEND EXTERNAL_LIBS spdlog)
if (ENABLE_LUAJIT)
list(APPEND EXTERNAL_LIBS luajit)
diff --git a/NOTICE b/NOTICE
index 4ed50a5ea..dea7cdaf4 100644
--- a/NOTICE
+++ b/NOTICE
@@ -28,6 +28,10 @@ NB: RocksDB is dual-licensed under both the GPLv2 and Apache
2.0 License.
This product uses it under the Apache 2.0 License.
* oneTBB(https://github.com/oneapi-src/oneTBB)
+* fast_float(https://github.com/fastfloat/fast_float)
+
+NB: fast_float is available under Apache-2.0, MIT, or Boost Software License
Version 1.0.
+This product uses it under the Apache 2.0 License and reuses the text in the
root LICENSE file.
Files src/common/rocksdb_crc32c.h and src/storage/batch_debugger.h are
modified from RocksDB.
Files src/types/bloom_filter.* are modified from Apache Arrow.
diff --git a/cmake/fast_float.cmake b/cmake/fast_float.cmake
new file mode 100644
index 000000000..8638a4a85
--- /dev/null
+++ b/cmake/fast_float.cmake
@@ -0,0 +1,31 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+include_guard()
+
+include(cmake/utils.cmake)
+
+FetchContent_DeclareGitHubWithMirror(fast_float
+ fastfloat/fast_float v8.2.4
+ MD5=0744790f2d40c9a2e5ef021476e59796
+)
+
+FetchContent_MakeAvailableWithArgs(fast_float
+ FASTFLOAT_TEST=OFF
+ FASTFLOAT_BENCHMARKS=OFF
+ FASTFLOAT_INSTALL=OFF
+)
diff --git a/src/common/parse_util.h b/src/common/parse_util.h
index 4c5ea8507..95b1fd598 100644
--- a/src/common/parse_util.h
+++ b/src/common/parse_util.h
@@ -21,9 +21,11 @@
#pragma once
#include <charconv>
+#include <string_view>
#include <tuple>
#include <type_traits>
+#include "fast_float/fast_float.h"
#include "status.h"
template <typename T>
@@ -91,53 +93,32 @@ StatusOr<T> ParseInt(std::string_view v, NumericRange<T>
range, int base = 10) {
// available units: K, M, G, T, P
StatusOr<std::uint64_t> ParseSizeAndUnit(std::string_view v);
-// we cannot use std::from_chars for floating-point numbers,
-// since it is available since gcc/libstdc++ 11 and libc++ 20.
-template <typename>
-struct ParseFloatFunc;
-
-template <>
-struct ParseFloatFunc<float> {
- constexpr static const auto value = strtof;
-};
-
-template <>
-struct ParseFloatFunc<double> {
- constexpr static const auto value = strtod;
-};
-
-template <>
-struct ParseFloatFunc<long double> {
- constexpr static const auto value = strtold;
-};
-
// TryParseFloat parses a string to a floating-point number,
// it returns the first unmatched character position instead of an error status
-template <typename T = double> // float or double
-StatusOr<ParseResultAndPos<T>> TryParseFloat(const char *str) {
- char *end = nullptr;
+template <typename T = double, std::enable_if_t<std::is_same_v<T, float> ||
std::is_same_v<T, double>, int> = 0>
+StatusOr<ParseResultAndPos<T>> TryParseFloat(std::string_view v) {
+ T result = 0;
+ auto [end, ec] =
+ fast_float::from_chars(v.data(), v.data() + v.size(), result,
+ fast_float::chars_format::general |
fast_float::chars_format::allow_leading_plus);
- errno = 0;
- T result = ParseFloatFunc<T>::value(str, &end);
-
- if (str == end) {
+ if (v.data() == end) {
return {Status::NotOK, "not started as a number"};
}
- if (errno) {
- return Status::FromErrno();
+ if (ec != std::errc()) {
+ return {Status::NotOK, std::make_error_code(ec).message()};
}
return {result, end};
}
// ParseFloat parses a string to a floating-point number
-template <typename T = double> // float or double
-StatusOr<T> ParseFloat(const std::string &str) {
- const char *begin = str.c_str();
- auto [result, pos] = GET_OR_RET(TryParseFloat<T>(begin));
+template <typename T = double>
+StatusOr<T> ParseFloat(std::string_view str) {
+ auto [result, pos] = GET_OR_RET(TryParseFloat<T>(str));
- if (pos != begin + str.size()) {
+ if (pos != str.data() + str.size()) {
return {Status::NotOK, "encounter non-number characters"};
}
diff --git a/tests/cppunit/parse_util.cc b/tests/cppunit/parse_util.cc
index 3dd00de64..ea64b9f61 100644
--- a/tests/cppunit/parse_util.cc
+++ b/tests/cppunit/parse_util.cc
@@ -21,6 +21,8 @@
#include <gtest/gtest.h>
#include <parse_util.h>
+#include <cmath>
+
TEST(ParseUtil, TryParseInt) {
long long v = 0;
const char *str = "12345hellooo", *end = nullptr;
@@ -70,21 +72,24 @@ TEST(ParseUtil, ParseSizeAndUnit) {
TEST(ParseUtil, ParseFloat) {
std::string v = "1.23";
- ASSERT_EQ(*TryParseFloat(v.c_str()), ParseResultAndPos<double>(1.23,
v.c_str() + v.size()));
+ ASSERT_EQ(*TryParseFloat(v), ParseResultAndPos<double>(1.23, v.c_str() +
v.size()));
v = "25345.346e65hello";
- ASSERT_EQ(*TryParseFloat(v.c_str()), ParseResultAndPos<double>(25345.346e65,
v.c_str() + v.size() - 5));
+ ASSERT_EQ(*TryParseFloat(v), ParseResultAndPos<double>(25345.346e65,
v.c_str() + v.size() - 5));
ASSERT_FALSE(TryParseFloat("eeeeeeee"));
ASSERT_FALSE(TryParseFloat(" "));
ASSERT_FALSE(TryParseFloat(""));
ASSERT_FALSE(TryParseFloat(" abcd"));
- v = " 1e8 ";
- ASSERT_EQ(*TryParseFloat(v.c_str()), ParseResultAndPos<double>(1e8,
v.c_str() + v.size() - 3));
+ v = "1e8 ";
+ ASSERT_EQ(*TryParseFloat(v), ParseResultAndPos<double>(1e8, v.c_str() +
v.size() - 3));
ASSERT_EQ(*ParseFloat("1.23"), 1.23);
ASSERT_EQ(*ParseFloat("1.23e2"), 1.23e2);
+ ASSERT_TRUE(std::isinf(*ParseFloat("+inf")));
+ ASSERT_TRUE(std::isnan(*ParseFloat("nan")));
ASSERT_FALSE(ParseFloat("1.2 "));
ASSERT_FALSE(ParseFloat("1.2hello"));
+ ASSERT_FALSE(ParseFloat("1e100000"));
}