Skip to content

Commit ad630d9

Browse files
committed
2 parents 3bf6360 + 42f2548 commit ad630d9

11 files changed

Lines changed: 182 additions & 21 deletions

File tree

‎.github/workflows/on-release.yml‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717

1818
steps:
1919
- uses: actions/checkout@v7
20-
20+
2121
- name: Amalgamate fast_float.h
2222
run: |
2323
mkdir build
@@ -29,13 +29,13 @@ jobs:
2929
cp tests/string_test.cpp build/
3030
cd build
3131
g++ string_test.cpp
32+
./a.out
3233
3334
- name: Upload Release Asset
34-
uses: actions/upload-release-asset@v1
35+
# --clobber replaces an asset that was already attached by hand, which
36+
# would otherwise make the upload fail.
3537
env:
36-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37-
with:
38-
upload_url: ${{ github.event.release.upload_url }}
39-
asset_path: build/fast_float/fast_float.h
40-
asset_name: fast_float.h
41-
asset_content_type: text/plain
38+
GH_TOKEN: ${{ github.token }}
39+
run: |
40+
gh release upload "${{ github.event.release.tag_name }}" \
41+
build/fast_float/fast_float.h --clobber

‎.github/workflows/release.yml‎

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
name: Release
2+
3+
# Manually triggered. Bumps the version (patch, minor or major), regenerates
4+
# the amalgamated fast_float.h, runs the tests, commits the version bump to
5+
# main, tags it and publishes a GitHub release with fast_float.h attached.
6+
#
7+
# Releases created with GITHUB_TOKEN do not trigger other workflows, so
8+
# on-release.yml does not run for releases made here.
9+
10+
'on':
11+
workflow_dispatch:
12+
inputs:
13+
bump:
14+
description: 'Which part of the version to bump'
15+
required: true
16+
default: 'patch'
17+
type: choice
18+
options:
19+
- patch
20+
- minor
21+
- major
22+
23+
permissions:
24+
contents: write
25+
26+
concurrency:
27+
group: release
28+
cancel-in-progress: false
29+
30+
jobs:
31+
release:
32+
runs-on: ubuntu-latest
33+
steps:
34+
- uses: actions/checkout@v7
35+
with:
36+
ref: main
37+
fetch-depth: 0
38+
39+
- name: Compute new version
40+
id: version
41+
run: |
42+
current=$(git describe --abbrev=0 --tags)
43+
current=${current#v}
44+
IFS=. read -r major minor patch <<< "$current"
45+
case "${{ inputs.bump }}" in
46+
major) major=$((major + 1)); minor=0; patch=0 ;;
47+
minor) minor=$((minor + 1)); patch=0 ;;
48+
patch) patch=$((patch + 1)) ;;
49+
*) echo "unknown bump type"; exit 1 ;;
50+
esac
51+
new="$major.$minor.$patch"
52+
echo "current=$current" >> "$GITHUB_OUTPUT"
53+
echo "new=$new" >> "$GITHUB_OUTPUT"
54+
echo "Releasing $current -> $new (${{ inputs.bump }})"
55+
56+
- name: Bump version files and amalgamate
57+
run: |
58+
new="${{ steps.version.outputs.new }}"
59+
IFS=. read -r major minor patch <<< "$new"
60+
python3 script/release.py "$new"
61+
rm -f CMakeLists.txt.bak README.md.bak MODULE.bazel.bak \
62+
include/fast_float/float_common.h.bak
63+
grep -q "project(fast_float VERSION $new LANGUAGES CXX)" CMakeLists.txt
64+
grep -q "#define FASTFLOAT_VERSION_MAJOR $major$" include/fast_float/float_common.h
65+
grep -q "#define FASTFLOAT_VERSION_MINOR $minor$" include/fast_float/float_common.h
66+
grep -q "#define FASTFLOAT_VERSION_PATCH $patch$" include/fast_float/float_common.h
67+
grep -q "version = \"$new\"," MODULE.bazel
68+
grep -q "releases/download/v$new/fast_float.h" README.md
69+
grep -q "#define FASTFLOAT_VERSION_PATCH $patch$" fast_float.h
70+
71+
- name: Test amalgamation
72+
run: |
73+
mkdir -p amalgamation/fast_float
74+
cp fast_float.h amalgamation/fast_float/
75+
g++ -O2 -std=c++17 -Iamalgamation -o amalgamation/string_test tests/string_test.cpp
76+
./amalgamation/string_test
77+
78+
- name: Build and test
79+
run: |
80+
cmake -B build -DFASTFLOAT_TEST=ON -DCMAKE_CXX_FLAGS='-Werror -Wundef'
81+
cmake --build build --parallel
82+
ctest --test-dir build --output-on-failure
83+
84+
- name: Commit, tag and push
85+
run: |
86+
new="${{ steps.version.outputs.new }}"
87+
git config user.name "github-actions[bot]"
88+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
89+
git add CMakeLists.txt README.md MODULE.bazel include/fast_float/float_common.h
90+
git commit -m "Version $new"
91+
git tag -a "v$new" -m "version $new"
92+
git push origin main
93+
git push origin "v$new"
94+
95+
- name: Create GitHub release
96+
env:
97+
GH_TOKEN: ${{ github.token }}
98+
run: |
99+
new="${{ steps.version.outputs.new }}"
100+
gh release create "v$new" \
101+
--title "Version $new" \
102+
--generate-notes \
103+
--notes-start-tag "v${{ steps.version.outputs.current }}" \
104+
fast_float.h

‎CMakeLists.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.14)
22

33

4-
project(fast_float VERSION 8.3.0 LANGUAGES CXX)
4+
project(fast_float VERSION 8.3.2 LANGUAGES CXX)
55
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
66
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
77
option(FASTFLOAT_TEST "Enable tests" OFF)

‎MODULE.bazel‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
module(
44
name = "fast_float",
5-
version = "8.2.4",
5+
version = "8.3.2",
66
compatibility_level = 8,
77
)
88

‎README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ sufficiently recent version of CMake (3.11 or better at least):
702702
FetchContent_Declare(
703703
fast_float
704704
GIT_REPOSITORY https://github.com/irainman/fast_float.git
705-
GIT_TAG tags/v8.3.0
705+
GIT_TAG tags/v8.3.2
706706
GIT_SHALLOW TRUE)
707707
708708
FetchContent_MakeAvailable(fast_float)
@@ -718,7 +718,7 @@ You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so:
718718
CPMAddPackage(
719719
NAME fast_float
720720
GITHUB_REPOSITORY "irainman/fast_float"
721-
GIT_TAG v8.3.0)
721+
GIT_TAG v8.3.2)
722722
```
723723

724724
## Using as single header
@@ -730,7 +730,7 @@ if desired as described in the command line help.
730730

731731
You may directly download automatically generated single-header files:
732732

733-
<https://github.com/irainman/fast_float/releases/download/v8.3.0/fast_float.h>
733+
<https://github.com/irainman/fast_float/releases/download/v8.3.2/fast_float.h>
734734

735735
## Benchmarking
736736

‎include/fast_float/ascii_number.h‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@ fastfloat_inline FASTFLOAT_CONSTEVAL bool has_simd_opt() noexcept {
3030
// Next function can be micro-optimized, but compilers are entirely
3131
// able to optimize it well.
3232
template <typename UC>
33-
fastfloat_inline FASTFLOAT_CONSTEXPR14 bool is_integer(UC c) noexcept {
34-
const auto d = c - UC('0');
35-
// UC may be signed.
36-
return d >= 0 && d <= 9;
33+
fastfloat_inline constexpr bool is_integer(UC c) noexcept {
34+
// UC can be signed (wchar_t is a signed int on Linux and macOS), and the
35+
// subtraction is then promoted to int and overflows for code units near the
36+
// bottom of the range. Subtracting in the unsigned type wraps instead, which
37+
// selects the same code units, as ch_to_digit already does.
38+
using UnsignedUC = typename std::make_unsigned<UC>::type;
39+
return static_cast<UnsignedUC>(static_cast<UnsignedUC>(c) -
40+
static_cast<UnsignedUC>(UC('0'))) <= 9u;
3741
}
3842

3943
#if FASTFLOAT_IS_BIG_ENDIAN
@@ -470,9 +474,12 @@ enum class parse_error : uint_fast8_t {
470474

471475
template <typename UC> struct parsed_number_string_t {
472476
FASTFLOAT_NO_UNIQUE_ADDRESS am_mant_t mantissa;
473-
FASTFLOAT_NO_UNIQUE_ADDRESS UC const *lastmatch;
474477
FASTFLOAT_NO_UNIQUE_ADDRESS am_pow_t exponent;
478+
FASTFLOAT_NO_UNIQUE_ADDRESS UC const *lastmatch;
475479

480+
// The field order matters: placing 'error' next to the booleans avoids
481+
// padding, keeping the struct at 64 bytes on 64-bit systems instead of 72.
482+
// See https://github.com/fastfloat/fast_float/issues/418
476483
FASTFLOAT_NO_UNIQUE_ADDRESS parse_error error;
477484
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
478485
FASTFLOAT_NO_UNIQUE_ADDRESS bool negative;

‎include/fast_float/float_common.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
#define FASTFLOAT_VERSION_MAJOR 8
2020
#define FASTFLOAT_VERSION_MINOR 3
21-
#define FASTFLOAT_VERSION_PATCH 0
21+
#define FASTFLOAT_VERSION_PATCH 2
2222

2323
#define FASTFLOAT_STRINGIZE_IMPL(x) #x
2424
#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)

‎include/fast_float/parse_number.h‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,9 @@ FASTFLOAT_CONSTEXPR20
588588
int const decimal_exponent) noexcept {
589589
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
590590
const auto is_negative = mantissa < 0;
591-
const auto m = static_cast<am_mant_t>(is_negative ? -mantissa : mantissa);
591+
// Negate as unsigned: -mantissa overflows for the most negative int64_t.
592+
const auto m = is_negative ? am_mant_t(0) - static_cast<am_mant_t>(mantissa)
593+
: static_cast<am_mant_t>(mantissa);
592594
#else
593595
FASTFLOAT_ASSUME(mantissa >= 0);
594596
const auto m = static_cast<am_mant_t>(mantissa);

‎script/release.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,18 @@ def toversionstring(major, minor, rev):
145145

146146
print(f"{versionfile} modified")
147147

148+
bazelfile = f"{maindir}{os.sep}MODULE.bazel"
149+
150+
for line in fileinput.input(bazelfile, inplace=1, backup=".bak"):
151+
line = re.sub(
152+
r'^(\s*version = )"\d+\.\d+\.\d+",',
153+
rf'\g<1>"{newversionstring}",',
154+
line.rstrip(),
155+
)
156+
print(line)
157+
158+
print(f"modified {bazelfile}, a backup was made")
159+
148160
readmefile = f"{maindir}{os.sep}README.md"
149161

150162
for line in fileinput.input(readmefile, inplace=1, backup=".bak"):
@@ -173,6 +185,7 @@ def toversionstring(major, minor, rev):
173185

174186
if cp.returncode != 0:
175187
print("Failed to run amalgamate")
188+
sys.exit(cp.returncode)
176189
else:
177190
print("amalgamate.py ran successfully")
178191
print(f"You should upload {maindir}{os.sep}fast_float.h")

‎tests/basictest.cpp‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ constexpr std::string_view const round_name(int d) {
8080
}
8181
}
8282

83+
// Guard against padding regressions that hurt performance.
84+
// See https://github.com/fastfloat/fast_float/issues/418
85+
static_assert(sizeof(void *) != 8 ||
86+
sizeof(fast_float::parsed_number_string_t<char>) <= 64,
87+
"parsed_number_string_t should fit in 64 bytes");
88+
8389
#define FASTFLOAT_STR(x) #x
8490
#define SHOW_DEFINE(x) printf("%s='%s'\n", #x, FASTFLOAT_STR(x))
8591

@@ -2475,6 +2481,10 @@ TEST_CASE("integer_times_pow10") {
24752481
all::verify_integer_times_pow10(12345678901234567890ull, -42);
24762482
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::max(), 42);
24772483
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::max(), -42);
2484+
// the most negative int64_t has no positive counterpart to negate into
2485+
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::min(), 0);
2486+
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::min(), 42);
2487+
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::min(), -42);
24782488
all::verify_integer_times_pow10(std::numeric_limits<uint64_t>::max(), 42);
24792489
all::verify_integer_times_pow10(std::numeric_limits<uint64_t>::max(), -42);
24802490
}

0 commit comments

Comments
 (0)