mirror of
https://github.com/valkey-io/valkey.git
synced 2026-05-06 05:26:42 -04:00
Replace fast_float (C++) with ffc.h (#3329)
There is now a port of fast_float in C. So instead of having an optional fast_float dependency, we can just use ffc instead, unconditionally. https://github.com/kolemannix/ffc.h It is a high quality port. The performance should be the same or improved. Note : I am the maintainer and main author of fast_float. --------- Signed-off-by: Daniel Lemire <daniel@lemire.me>
This commit is contained in:
@@ -37,7 +37,7 @@ jobs:
|
||||
# build with TLS just for compilation coverage
|
||||
run: |
|
||||
sudo apt-get install pkg-config libgtest-dev libgmock-dev
|
||||
make -j4 all-with-unit-tests SERVER_CFLAGS='-Werror' BUILD_TLS=yes USE_FAST_FLOAT=yes USE_LIBBACKTRACE=yes
|
||||
make -j4 all-with-unit-tests SERVER_CFLAGS='-Werror' BUILD_TLS=yes USE_LIBBACKTRACE=yes
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6 tclx
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
- name: make
|
||||
# Fail build if there are warnings
|
||||
# build with TLS just for compilation coverage
|
||||
run: make -j4 all-with-unit-tests SERVER_CFLAGS='-Werror' BUILD_TLS=yes USE_FAST_FLOAT=yes USE_LIBBACKTRACE=yes
|
||||
run: make -j4 all-with-unit-tests SERVER_CFLAGS='-Werror' BUILD_TLS=yes USE_LIBBACKTRACE=yes
|
||||
|
||||
- name: Install old server (${{ matrix.server.version }}) for compatibility testing
|
||||
run: |
|
||||
@@ -238,7 +238,7 @@ jobs:
|
||||
export CXX=/opt/homebrew/opt/llvm/bin/clang++
|
||||
export AR=/opt/homebrew/opt/llvm/bin/llvm-ar
|
||||
export RANLIB=/opt/homebrew/opt/llvm/bin/llvm-ranlib
|
||||
make -j3 all-with-unit-tests SERVER_CFLAGS='-Werror' USE_FAST_FLOAT=yes USE_LIBBACKTRACE=yes LIBBACKTRACE_PREFIX=/usr/local
|
||||
make -j3 all-with-unit-tests SERVER_CFLAGS='-Werror' USE_LIBBACKTRACE=yes LIBBACKTRACE_PREFIX=/usr/local
|
||||
|
||||
build-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -278,7 +278,7 @@ jobs:
|
||||
# suffixed version of g++. g++-multilib generally includes libstdc++.
|
||||
# *cross version as well, but it is also added explicitly just in case.
|
||||
run: |
|
||||
make -j4 SERVER_CFLAGS='-Werror' 32bit USE_FAST_FLOAT=yes USE_LIBBACKTRACE=yes LIBBACKTRACE_PREFIX=/usr/local/libbacktrace32 \
|
||||
make -j4 SERVER_CFLAGS='-Werror' 32bit USE_LIBBACKTRACE=yes LIBBACKTRACE_PREFIX=/usr/local/libbacktrace32 \
|
||||
GTEST_CFLAGS="-I/usr/src/googletest/googletest/include -I/usr/src/googletest/googlemock/include" \
|
||||
GTEST_LIBS="/usr/lib32/libgtest.a /usr/lib32/libgmock.a"
|
||||
- name: unit tests
|
||||
@@ -298,7 +298,7 @@ jobs:
|
||||
- name: Checkout Valkey
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: make
|
||||
run: make -j4 SERVER_CFLAGS='-Werror' MALLOC=libc USE_FAST_FLOAT=yes USE_LIBBACKTRACE=yes
|
||||
run: make -j4 SERVER_CFLAGS='-Werror' MALLOC=libc USE_LIBBACKTRACE=yes
|
||||
|
||||
build-almalinux8-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -317,7 +317,7 @@ jobs:
|
||||
- name: Checkout Valkey
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: make
|
||||
run: make -j4 SERVER_CFLAGS='-Werror' USE_FAST_FLOAT=yes USE_LIBBACKTRACE=yes
|
||||
run: make -j4 SERVER_CFLAGS='-Werror' USE_LIBBACKTRACE=yes
|
||||
|
||||
format-yaml:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -539,7 +539,7 @@ jobs:
|
||||
path: libbacktrace
|
||||
- run: cd libbacktrace && ./configure && make && sudo make install
|
||||
- name: make
|
||||
run: make BUILD_TLS=yes SERVER_CFLAGS='-Werror' USE_LIBBACKTRACE=yes USE_FAST_FLOAT=yes
|
||||
run: make BUILD_TLS=yes SERVER_CFLAGS='-Werror' USE_LIBBACKTRACE=yes
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get install tcl8.6 tclx tcl-tls
|
||||
|
||||
@@ -53,13 +53,6 @@ as libsystemd-dev on Debian/Ubuntu or systemd-devel on CentOS) and run:
|
||||
|
||||
% make USE_SYSTEMD=yes
|
||||
|
||||
Since Valkey version 8.1, `fast_float` has been introduced as an optional
|
||||
dependency, which can speed up sorted sets and other commands that use
|
||||
the double datatype. To build with `fast_float` support, you'll need a
|
||||
C++ compiler and run:
|
||||
|
||||
% make USE_FAST_FLOAT=yes
|
||||
|
||||
To build with enhanced stack traces that include file names and line numbers
|
||||
for all functions (including static functions), use libbacktrace:
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ set(VALKEY_SERVER_SRCS
|
||||
${CMAKE_SOURCE_DIR}/src/sparkline.c
|
||||
${CMAKE_SOURCE_DIR}/src/valkey-check-rdb.c
|
||||
${CMAKE_SOURCE_DIR}/src/valkey-check-aof.c
|
||||
${CMAKE_SOURCE_DIR}/src/valkey_strtod.c
|
||||
${CMAKE_SOURCE_DIR}/src/geo.c
|
||||
${CMAKE_SOURCE_DIR}/src/lazyfree.c
|
||||
${CMAKE_SOURCE_DIR}/src/module.c
|
||||
@@ -132,6 +133,7 @@ set(VALKEY_CLI_SRCS
|
||||
${CMAKE_SOURCE_DIR}/src/sha256.c
|
||||
${CMAKE_SOURCE_DIR}/src/util.c
|
||||
${CMAKE_SOURCE_DIR}/src/valkey-cli.c
|
||||
${CMAKE_SOURCE_DIR}/src/valkey_strtod.c
|
||||
${CMAKE_SOURCE_DIR}/src/zmalloc.c
|
||||
${CMAKE_SOURCE_DIR}/src/release.c
|
||||
${CMAKE_SOURCE_DIR}/src/ae.c
|
||||
@@ -155,6 +157,7 @@ set(VALKEY_BENCHMARK_SRCS
|
||||
${CMAKE_SOURCE_DIR}/src/sha256.c
|
||||
${CMAKE_SOURCE_DIR}/src/util.c
|
||||
${CMAKE_SOURCE_DIR}/src/valkey-benchmark.c
|
||||
${CMAKE_SOURCE_DIR}/src/valkey_strtod.c
|
||||
${CMAKE_SOURCE_DIR}/src/adlist.c
|
||||
${CMAKE_SOURCE_DIR}/src/dict.c
|
||||
${CMAKE_SOURCE_DIR}/src/zmalloc.c
|
||||
|
||||
Vendored
+1
@@ -33,6 +33,7 @@ add_subdirectory(libvalkey)
|
||||
add_subdirectory(linenoise)
|
||||
add_subdirectory(fpconv)
|
||||
add_subdirectory(hdr_histogram)
|
||||
add_subdirectory(fast_float)
|
||||
|
||||
# Clear any cached variables passed to libvalkey from the cache
|
||||
unset(BUILD_SHARED_LIBS CACHE)
|
||||
|
||||
Vendored
-7
@@ -42,7 +42,6 @@ distclean:
|
||||
-(cd jemalloc && [ -f Makefile ] && $(MAKE) distclean) > /dev/null || true
|
||||
-(cd hdr_histogram && $(MAKE) clean) > /dev/null || true
|
||||
-(cd fpconv && $(MAKE) clean) > /dev/null || true
|
||||
-(cd fast_float_c_interface && $(MAKE) clean) > /dev/null || true
|
||||
-(rm -f .make-*)
|
||||
|
||||
.PHONY: distclean
|
||||
@@ -126,12 +125,6 @@ jemalloc: .make-prerequisites
|
||||
|
||||
.PHONY: jemalloc
|
||||
|
||||
fast_float_c_interface: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd fast_float_c_interface && $(MAKE)
|
||||
|
||||
.PHONY: fast_float_c_interface
|
||||
|
||||
gtest-parallel: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
@if [ ! -f gtest-parallel/gtest_parallel.py ]; then \
|
||||
|
||||
Vendored
+6
-12
@@ -6,7 +6,7 @@ should be provided by the operating system.
|
||||
* **linenoise** is a readline replacement. It is developed by the same authors of Valkey but is managed as a separated project and updated as needed.
|
||||
* **lua** is Lua 5.1 with minor changes for security and additional libraries.
|
||||
* **hdr_histogram** Used for per-command latency tracking histograms.
|
||||
* **fast_float** is a replacement for strtod to convert strings to floats efficiently.
|
||||
* **ffc.h** is a C99 port of the fast_float library, used as a replacement for strtod to convert strings to floats efficiently.
|
||||
* **gtest-parallel** is a script for running googletest tests in parallel.
|
||||
|
||||
How to upgrade the above dependencies
|
||||
@@ -108,20 +108,14 @@ We use a customized version based on master branch commit e4448cf6d1cd08fff51981
|
||||
2. Copy updated files from newer version onto files in /hdr_histogram.
|
||||
3. Apply the changes from 1 above to the updated files.
|
||||
|
||||
fast_float
|
||||
ffc.h
|
||||
---
|
||||
The fast_float library provides fast header-only implementations for the C++ from_chars functions for `float` and `double` types as well as integer types. These functions convert ASCII strings representing decimal values (e.g., `1.3e10`) into binary types. The functions are much faster than comparable number-parsing functions from existing C++ standard libraries.
|
||||
|
||||
Specifically, `fast_float` provides the following function to parse floating-point numbers with a C++17-like syntax (the library itself only requires C++11):
|
||||
|
||||
template <typename T, typename UC = char, typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>())>
|
||||
from_chars_result_t<UC> from_chars(UC const *first, UC const *last, T &value, chars_format fmt = chars_format::general);
|
||||
ffc.h is a pure C99 port of the fast_float library, providing fast string-to-double
|
||||
conversion without requiring a C++ compiler.
|
||||
|
||||
To upgrade the library,
|
||||
1. Check out https://github.com/fastfloat/fast_float/tree/main
|
||||
2. cd fast_float
|
||||
3. Invoke "python3 ./script/amalgamate.py --output fast_float.h"
|
||||
4. Copy fast_float.h file to "deps/fast_float/".
|
||||
1. Download the latest ffc.h from https://github.com/kolemannix/ffc.h/releases
|
||||
2. Copy ffc.h to "deps/fast_float/".
|
||||
|
||||
gtest-parallel
|
||||
---
|
||||
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
add_library(ffc INTERFACE)
|
||||
target_include_directories(ffc INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
Vendored
-3912
File diff suppressed because it is too large
Load Diff
Vendored
+3235
File diff suppressed because one or more lines are too long
Vendored
-37
@@ -1,37 +0,0 @@
|
||||
CCCOLOR:="\033[34m"
|
||||
SRCCOLOR:="\033[33m"
|
||||
ENDCOLOR:="\033[0m"
|
||||
|
||||
CXX?=c++
|
||||
# we need = instead of := so that $@ in QUIET_CXX gets evaluated in the rule and is assigned appropriate value.
|
||||
TEMP:=$(CXX)
|
||||
QUIET_CXX=@printf ' %b %b\n' $(CCCOLOR)C++$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2;
|
||||
CXX=$(QUIET_CXX)$(TEMP)
|
||||
|
||||
WARN=-Wall -W -Wno-missing-field-initializers
|
||||
|
||||
STD=-pedantic -std=c++11
|
||||
|
||||
OPT?=-O3
|
||||
CLANG := $(findstring clang,$(shell sh -c '$(CC) --version | head -1'))
|
||||
ifeq ($(OPT),-O3)
|
||||
ifeq (clang,$(CLANG))
|
||||
OPT+=-flto
|
||||
else
|
||||
OPT+=-flto=auto -ffat-lto-objects
|
||||
endif
|
||||
endif
|
||||
|
||||
# 1) Today src/Makefile passes -m32 flag for explicit 32-bit build on 64-bit machine, via CFLAGS. For 32-bit build on
|
||||
# 32-bit machine and 64-bit on 64-bit machine, CFLAGS are empty. No other flags are set that can conflict with C++,
|
||||
# therefore let's use CFLAGS without changes for now.
|
||||
# 2) FASTFLOAT_ALLOWS_LEADING_PLUS allows +inf to be parsed as inf, instead of error.
|
||||
CXXFLAGS=$(STD) $(OPT) $(WARN) -static -fPIC -fno-exceptions $(CFLAGS) -D FASTFLOAT_ALLOWS_LEADING_PLUS
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: fast_float_strtod.o
|
||||
|
||||
clean:
|
||||
rm -f *.o || true;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Valkey Contributors
|
||||
* All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "../fast_float/fast_float.h"
|
||||
#include <cerrno>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
double fast_float_strtod(const char *str, const char** endptr)
|
||||
{
|
||||
double temp = 0;
|
||||
auto answer = fast_float::from_chars(str, str + strlen(str), temp);
|
||||
if (answer.ec != std::errc()) {
|
||||
errno = (answer.ec == std::errc::result_out_of_range) ? ERANGE : EINVAL;
|
||||
}
|
||||
if (endptr) {
|
||||
*endptr = answer.ptr;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ message(STATUS "CFLAGS: ${CMAKE_C_FLAGS}")
|
||||
get_valkey_server_linker_option(VALKEY_SERVER_LDFLAGS)
|
||||
list(APPEND SERVER_LIBS "fpconv")
|
||||
list(APPEND SERVER_LIBS "hdr_histogram")
|
||||
list(APPEND SERVER_LIBS "ffc")
|
||||
valkey_build_and_install_bin(valkey-server "${VALKEY_SERVER_SRCS}" "${VALKEY_SERVER_LDFLAGS}" "${SERVER_LIBS}"
|
||||
"redis-server")
|
||||
add_dependencies(valkey-server generate_commands_def)
|
||||
@@ -60,6 +61,7 @@ unset(BUILD_SANITIZER CACHE)
|
||||
# Target: valkey-cli
|
||||
list(APPEND CLI_LIBS "fpconv")
|
||||
list(APPEND CLI_LIBS "linenoise")
|
||||
list(APPEND CLI_LIBS "ffc")
|
||||
valkey_build_and_install_bin(valkey-cli "${VALKEY_CLI_SRCS}" "${VALKEY_SERVER_LDFLAGS}" "${CLI_LIBS}" "redis-cli")
|
||||
add_dependencies(valkey-cli generate_commands_def)
|
||||
add_dependencies(valkey-cli generate_fmtargs_h)
|
||||
@@ -67,6 +69,7 @@ add_dependencies(valkey-cli generate_fmtargs_h)
|
||||
# Target: valkey-benchmark
|
||||
list(APPEND BENCH_LIBS "fpconv")
|
||||
list(APPEND BENCH_LIBS "hdr_histogram")
|
||||
list(APPEND BENCH_LIBS "ffc")
|
||||
valkey_build_and_install_bin(valkey-benchmark "${VALKEY_BENCHMARK_SRCS}" "${VALKEY_SERVER_LDFLAGS}" "${BENCH_LIBS}"
|
||||
"redis-benchmark")
|
||||
add_dependencies(valkey-benchmark generate_commands_def)
|
||||
|
||||
+4
-13
@@ -251,7 +251,7 @@ ifdef OPENSSL_PREFIX
|
||||
endif
|
||||
|
||||
# Include paths to dependencies
|
||||
FINAL_CFLAGS+= -I../deps/libvalkey/include -I../deps/linenoise -I../deps/hdr_histogram -I../deps/fpconv
|
||||
FINAL_CFLAGS+= -I../deps/libvalkey/include -I../deps/linenoise -I../deps/hdr_histogram -I../deps/fpconv -I../deps/fast_float
|
||||
|
||||
# Lua scripting engine module
|
||||
LUA_MODULE_NAME:=modules/lua/libvalkeylua.so
|
||||
@@ -556,6 +556,7 @@ ENGINE_SERVER_OBJ = \
|
||||
util.o \
|
||||
valkey-check-aof.o \
|
||||
valkey-check-rdb.o \
|
||||
valkey_strtod.o \
|
||||
vector.o \
|
||||
vset.o \
|
||||
ziplist.o \
|
||||
@@ -584,6 +585,7 @@ ENGINE_CLI_OBJ = \
|
||||
strl.o \
|
||||
util.o \
|
||||
valkey-cli.o \
|
||||
valkey_strtod.o \
|
||||
zmalloc.o
|
||||
ENGINE_BENCHMARK_NAME=$(ENGINE_NAME)-benchmark$(PROG_SUFFIX)
|
||||
ENGINE_BENCHMARK_OBJ = \
|
||||
@@ -609,6 +611,7 @@ ENGINE_BENCHMARK_OBJ = \
|
||||
strl.o \
|
||||
util.o \
|
||||
valkey-benchmark.o \
|
||||
valkey_strtod.o \
|
||||
zmalloc.o
|
||||
ENGINE_CHECK_RDB_NAME=$(ENGINE_NAME)-check-rdb$(PROG_SUFFIX)
|
||||
ENGINE_CHECK_AOF_NAME=$(ENGINE_NAME)-check-aof$(PROG_SUFFIX)
|
||||
@@ -616,17 +619,6 @@ ENGINE_LIB_NAME=lib$(ENGINE_NAME).a
|
||||
ENGINE_UNIT_GTESTS:=$(ENGINE_NAME)-unit-gtests$(PROG_SUFFIX)
|
||||
ALL_SOURCES=$(sort $(patsubst %.o,%.c,$(ENGINE_SERVER_OBJ) $(ENGINE_CLI_OBJ) $(ENGINE_BENCHMARK_OBJ)))
|
||||
|
||||
USE_FAST_FLOAT?=no
|
||||
ifeq ($(USE_FAST_FLOAT),yes)
|
||||
# valkey_strtod.h uses this flag to switch valkey_strtod function to fast_float_strtod,
|
||||
# therefore let's pass it to compiler for preprocessing.
|
||||
FINAL_CFLAGS += -D USE_FAST_FLOAT
|
||||
# next, let's build and add actual library containing fast_float_strtod function for linking.
|
||||
DEPENDENCY_TARGETS += fast_float_c_interface
|
||||
FAST_FLOAT_STRTOD_OBJECT := ../deps/fast_float_c_interface/fast_float_strtod.o
|
||||
FINAL_LIBS += $(FAST_FLOAT_STRTOD_OBJECT)
|
||||
endif
|
||||
|
||||
USE_LIBBACKTRACE?=no
|
||||
ifeq ($(USE_LIBBACKTRACE),yes)
|
||||
FINAL_CFLAGS += -DUSE_LIBBACKTRACE
|
||||
@@ -668,7 +660,6 @@ persist-settings: distclean
|
||||
echo BUILD_RDMA=$(BUILD_RDMA) >> .make-settings
|
||||
echo USE_SYSTEMD=$(USE_SYSTEMD) >> .make-settings
|
||||
echo BUILD_LUA=$(BUILD_LUA) >> .make-settings
|
||||
echo USE_FAST_FLOAT=$(USE_FAST_FLOAT) >> .make-settings
|
||||
echo USE_LIBBACKTRACE=$(USE_LIBBACKTRACE) >> .make-settings
|
||||
echo LIBBACKTRACE_PREFIX=$(LIBBACKTRACE_PREFIX) >> .make-settings
|
||||
echo CFLAGS=$(CFLAGS) >> .make-settings
|
||||
|
||||
+1
-1
@@ -880,7 +880,7 @@ void debugCommand(client *c) {
|
||||
"string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false");
|
||||
}
|
||||
} else if (!strcasecmp(objectGetVal(c->argv[1]), "sleep") && c->argc == 3) {
|
||||
double dtime = valkey_strtod(objectGetVal(c->argv[2]), NULL);
|
||||
double dtime = valkey_strtod_sds(objectGetVal(c->argv[2]), NULL);
|
||||
long long utime = dtime * 1000000;
|
||||
struct timespec tv;
|
||||
|
||||
|
||||
+1
-4
@@ -150,13 +150,10 @@ static int parseDouble(ReplyParser *parser, void *p_ctx) {
|
||||
const char *proto = parser->curr_location;
|
||||
const char *p = strchr(proto + 1, '\r');
|
||||
parser->curr_location = p + 2; /* for \r\n */
|
||||
char buf[MAX_LONG_DOUBLE_CHARS + 1];
|
||||
size_t len = p - proto - 1;
|
||||
double d = 0;
|
||||
if (len <= MAX_LONG_DOUBLE_CHARS) {
|
||||
memcpy(buf, proto + 1, len);
|
||||
buf[len] = '\0';
|
||||
d = valkey_strtod(buf, NULL); /* We expect a valid representation. */
|
||||
d = valkey_strtod_n(proto + 1, len, NULL); /* We expect a valid representation. */
|
||||
}
|
||||
parser->callbacks.double_callback(p_ctx, d, proto, parser->curr_location - proto);
|
||||
return C_OK;
|
||||
|
||||
+1
-1
@@ -484,7 +484,7 @@ void sortCommandGeneric(client *c, int readonly) {
|
||||
if (sdsEncodedObject(byval)) {
|
||||
char *eptr;
|
||||
errno = 0;
|
||||
vector[j].u.score = valkey_strtod(objectGetVal(byval), &eptr);
|
||||
vector[j].u.score = valkey_strtod_sds(objectGetVal(byval), &eptr);
|
||||
if (eptr[0] != '\0' || errno == ERANGE || errno == EINVAL || isnan(vector[j].u.score)) {
|
||||
int_conversion_error = 1;
|
||||
}
|
||||
|
||||
+11
-11
@@ -630,24 +630,28 @@ static int zslParseRange(robj *min, robj *max, zrangespec *spec) {
|
||||
if (min->encoding == OBJ_ENCODING_INT) {
|
||||
spec->min = (long)objectGetVal(min);
|
||||
} else {
|
||||
if (((char *)objectGetVal(min))[0] == '(') {
|
||||
spec->min = valkey_strtod((char *)objectGetVal(min) + 1, &eptr);
|
||||
char *s = objectGetVal(min);
|
||||
size_t len = sdslen(s);
|
||||
if (s[0] == '(') {
|
||||
spec->min = valkey_strtod_n(s + 1, len - 1, &eptr);
|
||||
if (eptr[0] != '\0' || isnan(spec->min)) return C_ERR;
|
||||
spec->minex = 1;
|
||||
} else {
|
||||
spec->min = valkey_strtod((char *)objectGetVal(min), &eptr);
|
||||
spec->min = valkey_strtod_n(s, len, &eptr);
|
||||
if (eptr[0] != '\0' || isnan(spec->min)) return C_ERR;
|
||||
}
|
||||
}
|
||||
if (max->encoding == OBJ_ENCODING_INT) {
|
||||
spec->max = (long)objectGetVal(max);
|
||||
} else {
|
||||
if (((char *)objectGetVal(max))[0] == '(') {
|
||||
spec->max = valkey_strtod((char *)objectGetVal(max) + 1, &eptr);
|
||||
char *s = objectGetVal(max);
|
||||
size_t len = sdslen(s);
|
||||
if (s[0] == '(') {
|
||||
spec->max = valkey_strtod_n(s + 1, len - 1, &eptr);
|
||||
if (eptr[0] != '\0' || isnan(spec->max)) return C_ERR;
|
||||
spec->maxex = 1;
|
||||
} else {
|
||||
spec->max = valkey_strtod((char *)objectGetVal(max), &eptr);
|
||||
spec->max = valkey_strtod_n(s, len, &eptr);
|
||||
if (eptr[0] != '\0' || isnan(spec->max)) return C_ERR;
|
||||
}
|
||||
}
|
||||
@@ -841,11 +845,7 @@ zskiplistNode *zslNthInLexRange(zskiplist *zsl, zlexrangespec *range, long n) {
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static double zzlStrtod(unsigned char *vstr, unsigned int vlen) {
|
||||
char buf[128];
|
||||
if (vlen > sizeof(buf) - 1) vlen = sizeof(buf) - 1;
|
||||
memcpy(buf, vstr, vlen);
|
||||
buf[vlen] = '\0';
|
||||
return valkey_strtod(buf, NULL);
|
||||
return valkey_strtod_n((const char *)vstr, vlen, NULL);
|
||||
}
|
||||
|
||||
double zzlGetScore(unsigned char *sptr) {
|
||||
|
||||
@@ -43,6 +43,7 @@ add_library(valkeylib-gtest STATIC ${VALKEY_SERVER_SRCS})
|
||||
target_compile_options(valkeylib-gtest PRIVATE -Og -g -fno-lto)
|
||||
target_compile_definitions(valkeylib-gtest PRIVATE "${COMPILE_DEFINITIONS}")
|
||||
target_include_directories(valkeylib-gtest PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/deps)
|
||||
target_link_libraries(valkeylib-gtest ffc)
|
||||
|
||||
# Generate wrapper files from wrappers.h
|
||||
set(GENERATED_WRAPPERS_DIR ${CMAKE_BINARY_DIR}/gtest_generated)
|
||||
|
||||
@@ -105,7 +105,6 @@ endif
|
||||
ifeq ($(strip $(GTEST_LIBS)),)
|
||||
$(error googletest not found. Install gtest or set GTEST_CFLAGS/GTEST_LIBS explicitly)
|
||||
endif
|
||||
FAST_FLOAT_LIB := ../../deps/fast_float_c_interface/fast_float_strtod.o
|
||||
|
||||
# On Darwin (macOS), the linker does not support -Wl,--wrap,
|
||||
# so we manually define __wrap_* and __real_* for each function in wrappers.h.
|
||||
@@ -167,11 +166,6 @@ ifeq ($(USE_LIBBACKTRACE),yes)
|
||||
LD_LIBS += -lbacktrace
|
||||
endif
|
||||
|
||||
# Add fast_float support if enabled
|
||||
ifeq ($(USE_FAST_FLOAT),yes)
|
||||
LD_LIBS += $(FAST_FLOAT_LIB)
|
||||
endif
|
||||
|
||||
# Add TLS libraries if enabled
|
||||
ifeq ($(BUILD_TLS),yes)
|
||||
LD_LIBS += $(TLS_LIBS)
|
||||
|
||||
@@ -33,3 +33,22 @@ TEST_F(ValkeyStrtodTest, TestValkeyStrtod) {
|
||||
ASSERT_TRUE(isinf(value));
|
||||
ASSERT_EQ(errno, 0);
|
||||
}
|
||||
|
||||
TEST_F(ValkeyStrtodTest, TestValkeyStrtodN) {
|
||||
errno = 0;
|
||||
double value = valkey_strtod_n("231.2341234", 11, NULL);
|
||||
ASSERT_DOUBLE_EQ(value, 231.2341234);
|
||||
ASSERT_EQ(errno, 0);
|
||||
|
||||
value = valkey_strtod_n("+inf", 4, NULL);
|
||||
ASSERT_TRUE(isinf(value));
|
||||
ASSERT_EQ(errno, 0);
|
||||
|
||||
value = valkey_strtod_n("-inf", 4, NULL);
|
||||
ASSERT_TRUE(isinf(value));
|
||||
ASSERT_EQ(errno, 0);
|
||||
|
||||
value = valkey_strtod_n("inf", 3, NULL);
|
||||
ASSERT_TRUE(isinf(value));
|
||||
ASSERT_EQ(errno, 0);
|
||||
}
|
||||
|
||||
+1
-1
@@ -768,7 +768,7 @@ int string2ld(const char *s, size_t slen, long double *dp) {
|
||||
int string2d(const char *s, size_t slen, double *dp) {
|
||||
errno = 0;
|
||||
char *eptr;
|
||||
*dp = valkey_strtod(s, &eptr);
|
||||
*dp = valkey_strtod_n(s, slen, &eptr);
|
||||
if (slen == 0 || isspace(((const char *)s)[0]) || (size_t)(eptr - (char *)s) != slen ||
|
||||
(errno == ERANGE && (*dp == HUGE_VAL || *dp == -HUGE_VAL || fpclassify(*dp) == FP_ZERO)) || isnan(*dp) || errno == EINVAL) {
|
||||
errno = 0;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "valkey_strtod.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "sds.h"
|
||||
|
||||
#define FFC_IMPL
|
||||
#define FFC_DEBUG 0
|
||||
#include "ffc.h"
|
||||
|
||||
const ffc_parse_options valkey_strtod_options = {
|
||||
FFC_PRESET_GENERAL | FFC_FORMAT_FLAG_ALLOW_LEADING_PLUS,
|
||||
'.'};
|
||||
|
||||
/**
|
||||
* Converts a null-terminated string to a double-precision floating-point number.
|
||||
* On success, returns the converted value and sets *endptr to point past the
|
||||
* last parsed character. On failure, returns 0.0 and sets errno appropriately.
|
||||
*/
|
||||
double valkey_strtod(const char *str, char **endptr) {
|
||||
errno = 0;
|
||||
double temp = 0.0;
|
||||
ffc_result answer = ffc_from_chars_double_options(str, str + strlen(str), &temp, valkey_strtod_options);
|
||||
if (answer.outcome != FFC_OUTCOME_OK) {
|
||||
errno = (answer.outcome == FFC_OUTCOME_OUT_OF_RANGE) ? ERANGE : EINVAL;
|
||||
}
|
||||
if (endptr) {
|
||||
*endptr = (char *)answer.ptr;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string of specified length to a double-precision floating-point number.
|
||||
* Unlike valkey_strtod, this function does not require the string to be null-terminated,
|
||||
* making it suitable for parsing substrings. On success, returns the converted value
|
||||
* and sets *endptr to point past the last parsed character. On failure, returns 0.0
|
||||
* and sets errno appropriately.
|
||||
*/
|
||||
double valkey_strtod_n(const char *str, size_t len, char **endptr) {
|
||||
errno = 0;
|
||||
double temp = 0.0;
|
||||
ffc_result answer = ffc_from_chars_double_options(str, str + len, &temp, valkey_strtod_options);
|
||||
if (answer.outcome != FFC_OUTCOME_OK) {
|
||||
errno = (answer.outcome == FFC_OUTCOME_OUT_OF_RANGE) ? ERANGE : EINVAL;
|
||||
}
|
||||
if (endptr) {
|
||||
*endptr = (char *)answer.ptr;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an SDS string to a double-precision floating-point number.
|
||||
* This is a convenience wrapper around valkey_strtod_n that automatically
|
||||
* determines the string length using sdslen(). On success, returns the converted
|
||||
* value and sets *endptr to point past the last parsed character. On failure,
|
||||
* returns 0.0 and sets errno appropriately.
|
||||
*/
|
||||
double valkey_strtod_sds(sds str, char **endptr) {
|
||||
errno = 0;
|
||||
return valkey_strtod_n(str, sdslen(str), endptr);
|
||||
}
|
||||
+33
-32
@@ -1,42 +1,43 @@
|
||||
#ifndef FAST_FLOAT_STRTOD_H
|
||||
#define FAST_FLOAT_STRTOD_H
|
||||
#ifndef VALKEY_STRTOD_H
|
||||
#define VALKEY_STRTOD_H
|
||||
|
||||
#ifdef USE_FAST_FLOAT
|
||||
|
||||
#include "errno.h"
|
||||
#include "sds.h"
|
||||
|
||||
/**
|
||||
* Converts a null-terminated byte string to a double using the fast_float library.
|
||||
* Converts a string to a double using ffc.h (https://github.com/kolemannix/ffc.h),
|
||||
* a C99 port of the fast_float library.
|
||||
*
|
||||
* This function provides a C-compatible wrapper around the fast_float library's string-to-double
|
||||
* conversion functionality. It aims to offer a faster alternative to the standard strtod function.
|
||||
* valkey_strtod: takes a null-terminated string.
|
||||
* valkey_strtod_n: takes a pointer and length, avoiding strlen.
|
||||
*
|
||||
* str: A pointer to the null-terminated byte string to be converted.
|
||||
* eptr: On success, stores char pointer pointing to '\0' at the end of the string.
|
||||
* On failure, stores char pointer pointing to first invalid character in the string.
|
||||
* returns: On success, the function returns the converted double value.
|
||||
* On failure, it returns 0.0 and stores error code in errno to ERANGE or EINVAL.
|
||||
*
|
||||
* note: This function uses the fast_float library (https://github.com/fastfloat/fast_float) for
|
||||
* the actual conversion, which can be significantly faster than standard library functions.
|
||||
* Refer to "../deps/fast_float_c_interface" for more details.
|
||||
* Refer to https://github.com/fastfloat/fast_float for more information on the underlying library.
|
||||
* On success, returns the converted value and sets *endptr past the parsed characters.
|
||||
* On failure, returns 0.0, sets errno to ERANGE or EINVAL, and sets *endptr to
|
||||
* the first invalid character.
|
||||
*/
|
||||
double fast_float_strtod(const char *str, char **endptr);
|
||||
|
||||
static inline double valkey_strtod(const char *str, char **endptr) {
|
||||
errno = 0;
|
||||
return fast_float_strtod(str, endptr);
|
||||
}
|
||||
/**
|
||||
* Converts a null-terminated string to a double-precision floating-point number.
|
||||
* On success, returns the converted value and sets *endptr to point past the
|
||||
* last parsed character. On failure, returns 0.0 and sets errno appropriately.
|
||||
*/
|
||||
double valkey_strtod(const char *str, char **endptr);
|
||||
|
||||
#else
|
||||
/**
|
||||
* Converts a string of specified length to a double-precision floating-point number.
|
||||
* Unlike valkey_strtod, this function does not require the string to be null-terminated,
|
||||
* making it suitable for parsing substrings. On success, returns the converted value
|
||||
* and sets *endptr to point past the last parsed character. On failure, returns 0.0
|
||||
* and sets errno appropriately.
|
||||
*/
|
||||
double valkey_strtod_n(const char *str, size_t len, char **endptr);
|
||||
|
||||
#include <stdlib.h>
|
||||
/**
|
||||
* Converts an SDS string to a double-precision floating-point number.
|
||||
* This is a convenience wrapper around valkey_strtod_n that automatically
|
||||
* determines the string length using sdslen(). On success, returns the converted
|
||||
* value and sets *endptr to point past the last parsed character. On failure,
|
||||
* returns 0.0 and sets errno appropriately.
|
||||
*/
|
||||
double valkey_strtod_sds(sds str, char **endptr);
|
||||
|
||||
static inline double valkey_strtod(const char *str, char **endptr) {
|
||||
return strtod(str, endptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // FAST_FLOAT_STRTOD_H
|
||||
#endif // VALKEY_STRTOD_H
|
||||
|
||||
Reference in New Issue
Block a user