mirror of
https://github.com/fastfloat/fast_float.git
synced 2026-11-09 14:49:59 +08:00
Merge branch 'main' of https://github.com/fastfloat/fast_float
This commit is contained in:
commit
ad630d9c2e
14
.github/workflows/on-release.yml
vendored
14
.github/workflows/on-release.yml
vendored
@ -29,13 +29,13 @@ jobs:
|
|||||||
cp tests/string_test.cpp build/
|
cp tests/string_test.cpp build/
|
||||||
cd build
|
cd build
|
||||||
g++ string_test.cpp
|
g++ string_test.cpp
|
||||||
|
./a.out
|
||||||
|
|
||||||
- name: Upload Release Asset
|
- name: Upload Release Asset
|
||||||
uses: actions/upload-release-asset@v1
|
# --clobber replaces an asset that was already attached by hand, which
|
||||||
|
# would otherwise make the upload fail.
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
with:
|
run: |
|
||||||
upload_url: ${{ github.event.release.upload_url }}
|
gh release upload "${{ github.event.release.tag_name }}" \
|
||||||
asset_path: build/fast_float/fast_float.h
|
build/fast_float/fast_float.h --clobber
|
||||||
asset_name: fast_float.h
|
|
||||||
asset_content_type: text/plain
|
|
||||||
|
|||||||
104
.github/workflows/release.yml
vendored
Normal file
104
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
# Manually triggered. Bumps the version (patch, minor or major), regenerates
|
||||||
|
# the amalgamated fast_float.h, runs the tests, commits the version bump to
|
||||||
|
# main, tags it and publishes a GitHub release with fast_float.h attached.
|
||||||
|
#
|
||||||
|
# Releases created with GITHUB_TOKEN do not trigger other workflows, so
|
||||||
|
# on-release.yml does not run for releases made here.
|
||||||
|
|
||||||
|
'on':
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
bump:
|
||||||
|
description: 'Which part of the version to bump'
|
||||||
|
required: true
|
||||||
|
default: 'patch'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- patch
|
||||||
|
- minor
|
||||||
|
- major
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: release
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
ref: main
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Compute new version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
current=$(git describe --abbrev=0 --tags)
|
||||||
|
current=${current#v}
|
||||||
|
IFS=. read -r major minor patch <<< "$current"
|
||||||
|
case "${{ inputs.bump }}" in
|
||||||
|
major) major=$((major + 1)); minor=0; patch=0 ;;
|
||||||
|
minor) minor=$((minor + 1)); patch=0 ;;
|
||||||
|
patch) patch=$((patch + 1)) ;;
|
||||||
|
*) echo "unknown bump type"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
new="$major.$minor.$patch"
|
||||||
|
echo "current=$current" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "new=$new" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Releasing $current -> $new (${{ inputs.bump }})"
|
||||||
|
|
||||||
|
- name: Bump version files and amalgamate
|
||||||
|
run: |
|
||||||
|
new="${{ steps.version.outputs.new }}"
|
||||||
|
IFS=. read -r major minor patch <<< "$new"
|
||||||
|
python3 script/release.py "$new"
|
||||||
|
rm -f CMakeLists.txt.bak README.md.bak MODULE.bazel.bak \
|
||||||
|
include/fast_float/float_common.h.bak
|
||||||
|
grep -q "project(fast_float VERSION $new LANGUAGES CXX)" CMakeLists.txt
|
||||||
|
grep -q "#define FASTFLOAT_VERSION_MAJOR $major$" include/fast_float/float_common.h
|
||||||
|
grep -q "#define FASTFLOAT_VERSION_MINOR $minor$" include/fast_float/float_common.h
|
||||||
|
grep -q "#define FASTFLOAT_VERSION_PATCH $patch$" include/fast_float/float_common.h
|
||||||
|
grep -q "version = \"$new\"," MODULE.bazel
|
||||||
|
grep -q "releases/download/v$new/fast_float.h" README.md
|
||||||
|
grep -q "#define FASTFLOAT_VERSION_PATCH $patch$" fast_float.h
|
||||||
|
|
||||||
|
- name: Test amalgamation
|
||||||
|
run: |
|
||||||
|
mkdir -p amalgamation/fast_float
|
||||||
|
cp fast_float.h amalgamation/fast_float/
|
||||||
|
g++ -O2 -std=c++17 -Iamalgamation -o amalgamation/string_test tests/string_test.cpp
|
||||||
|
./amalgamation/string_test
|
||||||
|
|
||||||
|
- name: Build and test
|
||||||
|
run: |
|
||||||
|
cmake -B build -DFASTFLOAT_TEST=ON -DCMAKE_CXX_FLAGS='-Werror -Wundef'
|
||||||
|
cmake --build build --parallel
|
||||||
|
ctest --test-dir build --output-on-failure
|
||||||
|
|
||||||
|
- name: Commit, tag and push
|
||||||
|
run: |
|
||||||
|
new="${{ steps.version.outputs.new }}"
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add CMakeLists.txt README.md MODULE.bazel include/fast_float/float_common.h
|
||||||
|
git commit -m "Version $new"
|
||||||
|
git tag -a "v$new" -m "version $new"
|
||||||
|
git push origin main
|
||||||
|
git push origin "v$new"
|
||||||
|
|
||||||
|
- name: Create GitHub release
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
new="${{ steps.version.outputs.new }}"
|
||||||
|
gh release create "v$new" \
|
||||||
|
--title "Version $new" \
|
||||||
|
--generate-notes \
|
||||||
|
--notes-start-tag "v${{ steps.version.outputs.current }}" \
|
||||||
|
fast_float.h
|
||||||
@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.14)
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
|
||||||
|
|
||||||
project(fast_float VERSION 8.3.0 LANGUAGES CXX)
|
project(fast_float VERSION 8.3.2 LANGUAGES CXX)
|
||||||
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
|
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
|
||||||
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
|
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
|
||||||
option(FASTFLOAT_TEST "Enable tests" OFF)
|
option(FASTFLOAT_TEST "Enable tests" OFF)
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
module(
|
module(
|
||||||
name = "fast_float",
|
name = "fast_float",
|
||||||
version = "8.2.4",
|
version = "8.3.2",
|
||||||
compatibility_level = 8,
|
compatibility_level = 8,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -702,7 +702,7 @@ sufficiently recent version of CMake (3.11 or better at least):
|
|||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
fast_float
|
fast_float
|
||||||
GIT_REPOSITORY https://github.com/irainman/fast_float.git
|
GIT_REPOSITORY https://github.com/irainman/fast_float.git
|
||||||
GIT_TAG tags/v8.3.0
|
GIT_TAG tags/v8.3.2
|
||||||
GIT_SHALLOW TRUE)
|
GIT_SHALLOW TRUE)
|
||||||
|
|
||||||
FetchContent_MakeAvailable(fast_float)
|
FetchContent_MakeAvailable(fast_float)
|
||||||
@ -718,7 +718,7 @@ You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so:
|
|||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
NAME fast_float
|
NAME fast_float
|
||||||
GITHUB_REPOSITORY "irainman/fast_float"
|
GITHUB_REPOSITORY "irainman/fast_float"
|
||||||
GIT_TAG v8.3.0)
|
GIT_TAG v8.3.2)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using as single header
|
## Using as single header
|
||||||
@ -730,7 +730,7 @@ if desired as described in the command line help.
|
|||||||
|
|
||||||
You may directly download automatically generated single-header files:
|
You may directly download automatically generated single-header files:
|
||||||
|
|
||||||
<https://github.com/irainman/fast_float/releases/download/v8.3.0/fast_float.h>
|
<https://github.com/irainman/fast_float/releases/download/v8.3.2/fast_float.h>
|
||||||
|
|
||||||
## Benchmarking
|
## Benchmarking
|
||||||
|
|
||||||
|
|||||||
@ -30,10 +30,14 @@ fastfloat_inline FASTFLOAT_CONSTEVAL bool has_simd_opt() noexcept {
|
|||||||
// Next function can be micro-optimized, but compilers are entirely
|
// Next function can be micro-optimized, but compilers are entirely
|
||||||
// able to optimize it well.
|
// able to optimize it well.
|
||||||
template <typename UC>
|
template <typename UC>
|
||||||
fastfloat_inline FASTFLOAT_CONSTEXPR14 bool is_integer(UC c) noexcept {
|
fastfloat_inline constexpr bool is_integer(UC c) noexcept {
|
||||||
const auto d = c - UC('0');
|
// UC can be signed (wchar_t is a signed int on Linux and macOS), and the
|
||||||
// UC may be signed.
|
// subtraction is then promoted to int and overflows for code units near the
|
||||||
return d >= 0 && d <= 9;
|
// bottom of the range. Subtracting in the unsigned type wraps instead, which
|
||||||
|
// selects the same code units, as ch_to_digit already does.
|
||||||
|
using UnsignedUC = typename std::make_unsigned<UC>::type;
|
||||||
|
return static_cast<UnsignedUC>(static_cast<UnsignedUC>(c) -
|
||||||
|
static_cast<UnsignedUC>(UC('0'))) <= 9u;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FASTFLOAT_IS_BIG_ENDIAN
|
#if FASTFLOAT_IS_BIG_ENDIAN
|
||||||
@ -470,9 +474,12 @@ enum class parse_error : uint_fast8_t {
|
|||||||
|
|
||||||
template <typename UC> struct parsed_number_string_t {
|
template <typename UC> struct parsed_number_string_t {
|
||||||
FASTFLOAT_NO_UNIQUE_ADDRESS am_mant_t mantissa;
|
FASTFLOAT_NO_UNIQUE_ADDRESS am_mant_t mantissa;
|
||||||
FASTFLOAT_NO_UNIQUE_ADDRESS UC const *lastmatch;
|
|
||||||
FASTFLOAT_NO_UNIQUE_ADDRESS am_pow_t exponent;
|
FASTFLOAT_NO_UNIQUE_ADDRESS am_pow_t exponent;
|
||||||
|
FASTFLOAT_NO_UNIQUE_ADDRESS UC const *lastmatch;
|
||||||
|
|
||||||
|
// The field order matters: placing 'error' next to the booleans avoids
|
||||||
|
// padding, keeping the struct at 64 bytes on 64-bit systems instead of 72.
|
||||||
|
// See https://github.com/fastfloat/fast_float/issues/418
|
||||||
FASTFLOAT_NO_UNIQUE_ADDRESS parse_error error;
|
FASTFLOAT_NO_UNIQUE_ADDRESS parse_error error;
|
||||||
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
FASTFLOAT_NO_UNIQUE_ADDRESS bool negative;
|
FASTFLOAT_NO_UNIQUE_ADDRESS bool negative;
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
#define FASTFLOAT_VERSION_MAJOR 8
|
#define FASTFLOAT_VERSION_MAJOR 8
|
||||||
#define FASTFLOAT_VERSION_MINOR 3
|
#define FASTFLOAT_VERSION_MINOR 3
|
||||||
#define FASTFLOAT_VERSION_PATCH 0
|
#define FASTFLOAT_VERSION_PATCH 2
|
||||||
|
|
||||||
#define FASTFLOAT_STRINGIZE_IMPL(x) #x
|
#define FASTFLOAT_STRINGIZE_IMPL(x) #x
|
||||||
#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)
|
#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)
|
||||||
|
|||||||
@ -588,7 +588,9 @@ FASTFLOAT_CONSTEXPR20
|
|||||||
int const decimal_exponent) noexcept {
|
int const decimal_exponent) noexcept {
|
||||||
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
const auto is_negative = mantissa < 0;
|
const auto is_negative = mantissa < 0;
|
||||||
const auto m = static_cast<am_mant_t>(is_negative ? -mantissa : mantissa);
|
// Negate as unsigned: -mantissa overflows for the most negative int64_t.
|
||||||
|
const auto m = is_negative ? am_mant_t(0) - static_cast<am_mant_t>(mantissa)
|
||||||
|
: static_cast<am_mant_t>(mantissa);
|
||||||
#else
|
#else
|
||||||
FASTFLOAT_ASSUME(mantissa >= 0);
|
FASTFLOAT_ASSUME(mantissa >= 0);
|
||||||
const auto m = static_cast<am_mant_t>(mantissa);
|
const auto m = static_cast<am_mant_t>(mantissa);
|
||||||
|
|||||||
@ -145,6 +145,18 @@ for line in fileinput.input(versionfile, inplace=1, backup=".bak"):
|
|||||||
|
|
||||||
print(f"{versionfile} modified")
|
print(f"{versionfile} modified")
|
||||||
|
|
||||||
|
bazelfile = f"{maindir}{os.sep}MODULE.bazel"
|
||||||
|
|
||||||
|
for line in fileinput.input(bazelfile, inplace=1, backup=".bak"):
|
||||||
|
line = re.sub(
|
||||||
|
r'^(\s*version = )"\d+\.\d+\.\d+",',
|
||||||
|
rf'\g<1>"{newversionstring}",',
|
||||||
|
line.rstrip(),
|
||||||
|
)
|
||||||
|
print(line)
|
||||||
|
|
||||||
|
print(f"modified {bazelfile}, a backup was made")
|
||||||
|
|
||||||
readmefile = f"{maindir}{os.sep}README.md"
|
readmefile = f"{maindir}{os.sep}README.md"
|
||||||
|
|
||||||
for line in fileinput.input(readmefile, inplace=1, backup=".bak"):
|
for line in fileinput.input(readmefile, inplace=1, backup=".bak"):
|
||||||
@ -173,6 +185,7 @@ with open(f"{maindir}{os.sep}fast_float.h", "w") as outfile:
|
|||||||
|
|
||||||
if cp.returncode != 0:
|
if cp.returncode != 0:
|
||||||
print("Failed to run amalgamate")
|
print("Failed to run amalgamate")
|
||||||
|
sys.exit(cp.returncode)
|
||||||
else:
|
else:
|
||||||
print("amalgamate.py ran successfully")
|
print("amalgamate.py ran successfully")
|
||||||
print(f"You should upload {maindir}{os.sep}fast_float.h")
|
print(f"You should upload {maindir}{os.sep}fast_float.h")
|
||||||
|
|||||||
@ -80,6 +80,12 @@ constexpr std::string_view const round_name(int d) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Guard against padding regressions that hurt performance.
|
||||||
|
// See https://github.com/fastfloat/fast_float/issues/418
|
||||||
|
static_assert(sizeof(void *) != 8 ||
|
||||||
|
sizeof(fast_float::parsed_number_string_t<char>) <= 64,
|
||||||
|
"parsed_number_string_t should fit in 64 bytes");
|
||||||
|
|
||||||
#define FASTFLOAT_STR(x) #x
|
#define FASTFLOAT_STR(x) #x
|
||||||
#define SHOW_DEFINE(x) printf("%s='%s'\n", #x, FASTFLOAT_STR(x))
|
#define SHOW_DEFINE(x) printf("%s='%s'\n", #x, FASTFLOAT_STR(x))
|
||||||
|
|
||||||
@ -2475,6 +2481,10 @@ TEST_CASE("integer_times_pow10") {
|
|||||||
all::verify_integer_times_pow10(12345678901234567890ull, -42);
|
all::verify_integer_times_pow10(12345678901234567890ull, -42);
|
||||||
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::max(), 42);
|
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::max(), 42);
|
||||||
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::max(), -42);
|
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::max(), -42);
|
||||||
|
// the most negative int64_t has no positive counterpart to negate into
|
||||||
|
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::min(), 0);
|
||||||
|
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::min(), 42);
|
||||||
|
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::min(), -42);
|
||||||
all::verify_integer_times_pow10(std::numeric_limits<uint64_t>::max(), 42);
|
all::verify_integer_times_pow10(std::numeric_limits<uint64_t>::max(), 42);
|
||||||
all::verify_integer_times_pow10(std::numeric_limits<uint64_t>::max(), -42);
|
all::verify_integer_times_pow10(std::numeric_limits<uint64_t>::max(), -42);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,9 +70,34 @@ bool test_non_space_with_space_low_byte() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// is_integer subtracts UC('0') from the code unit. When wchar_t is signed the
|
||||||
|
// subtraction is promoted to int and overflows for code units near the bottom
|
||||||
|
// of the range, so the sanitized build aborts on input the library is supposed
|
||||||
|
// to reject. Such a unit is not a digit, so both entry points must fail.
|
||||||
|
bool test_code_unit_below_zero_digit() {
|
||||||
|
if (!std::is_signed<wchar_t>::value) {
|
||||||
|
return true; // only reproducible where wchar_t is signed
|
||||||
|
}
|
||||||
|
wchar_t const input[] = {(std::numeric_limits<wchar_t>::min)(), L'1'};
|
||||||
|
double result;
|
||||||
|
auto answer = fast_float::from_chars(input, input + 2, result);
|
||||||
|
if (answer.ec != std::errc::invalid_argument) {
|
||||||
|
std::cerr << "a code unit below L'0' must not start a number\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
unsigned long long integer;
|
||||||
|
auto int_answer = fast_float::from_chars(input, input + 2, integer, 10);
|
||||||
|
if (int_answer.ec != std::errc::invalid_argument) {
|
||||||
|
std::cerr << "a code unit below L'0' must not start an integer\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
if (test_minus() && test_plus() && test_space() && test_nan() &&
|
if (test_minus() && test_plus() && test_space() && test_nan() &&
|
||||||
test_non_space_with_space_low_byte()) {
|
test_non_space_with_space_low_byte() &&
|
||||||
|
test_code_unit_below_zero_digit()) {
|
||||||
std::cout << "all ok" << std::endl;
|
std::cout << "all ok" << std::endl;
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user