diff options
-rw-r--r-- | .clang-tidy | 1 | ||||
-rw-r--r-- | asl/strings/BUILD.bazel | 20 | ||||
-rw-r--r-- | asl/strings/parse_number.cpp | 162 | ||||
-rw-r--r-- | asl/strings/parse_number.hpp | 34 | ||||
-rw-r--r-- | asl/strings/parse_number_float.cpp | 30 | ||||
-rw-r--r-- | asl/strings/parse_number_integer_table.py | 24 | ||||
-rw-r--r-- | asl/strings/parse_number_tests.cpp | 203 | ||||
-rw-r--r-- | asl/strings/string_view.hpp | 7 | ||||
-rw-r--r-- | vendor/dragonbox/BUILD.bazel | 2 | ||||
-rw-r--r-- | vendor/fast_float/BUILD.bazel | 25 | ||||
-rw-r--r-- | vendor/fast_float/LICENSE.txt | 27 | ||||
-rw-r--r-- | vendor/fast_float/fast_float.h | 4443 |
12 files changed, 4977 insertions, 1 deletions
diff --git a/.clang-tidy b/.clang-tidy index 598d27b..ed23f75 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -21,3 +21,4 @@ Checks: - "-cppcoreguidelines-pro-type-union-access" - "-*-copy-assignment-signature" - "-*-unconventional-assign-operator" + - "-readability-math-missing-parentheses" diff --git a/asl/strings/BUILD.bazel b/asl/strings/BUILD.bazel index 79fc631..d6b83b0 100644 --- a/asl/strings/BUILD.bazel +++ b/asl/strings/BUILD.bazel @@ -45,6 +45,24 @@ cc_library( visibility = ["//visibility:public"], ) +cc_library( + name = "parse_number", + hdrs = [ + "parse_number.hpp", + ], + srcs = [ + "parse_number_float.cpp", + "parse_number.cpp", + ], + deps = [ + "//asl/base", + "//asl/types:status", + ":string_view", + "//vendor/fast_float", + ], + visibility = ["//visibility:public"], +) + [cc_test( name = "%s_tests" % name, srcs = [ @@ -54,6 +72,7 @@ cc_library( ":string", ":string_builder", ":string_view", + ":parse_number", "//asl/tests:utils", "//asl/testing", ], @@ -61,4 +80,5 @@ cc_library( "string", "string_view", "string_builder", + "parse_number", ]] diff --git a/asl/strings/parse_number.cpp b/asl/strings/parse_number.cpp new file mode 100644 index 0000000..3b6b689 --- /dev/null +++ b/asl/strings/parse_number.cpp @@ -0,0 +1,162 @@ +// Copyright 2025 Steven Le Rouzic +// +// SPDX-License-Identifier: BSD-3-Clause + +#include "asl/strings/parse_number.hpp" + +namespace asl +{ + +bool parse_float_impl(const char** begin, const char* end, float*); +bool parse_double_impl(const char** begin, const char* end, double*); + +} // namespace asl + +asl::status_or<asl::parse_number_result<float>> asl::parse_float(asl::string_view sv) +{ + const auto* begin = sv.data(); + // NOLINTNEXTLINE(*-pointer-arithmetic) + const auto* end = begin + sv.size(); + if (float value{}; parse_float_impl(&begin, end, &value)) + { + return parse_number_result<float>{ + .value = value, + .remaining = string_view{begin, end}, + }; + } + return invalid_argument_error(); +} + +asl::status_or<asl::parse_number_result<double>> asl::parse_double(asl::string_view sv) +{ + const auto* begin = sv.data(); + // NOLINTNEXTLINE(*-pointer-arithmetic) + const auto* end = begin + sv.size(); + if (float value{}; parse_float_impl(&begin, end, &value)) + { + return parse_number_result<double>{ + .value = value, + .remaining = string_view{begin, end}, + }; + } + return invalid_argument_error(); +} + +namespace +{ + +constexpr int8_t kBase16Table[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +// @Todo Maybe monomorph this for common bases (2, 16, 10)? + +template<typename T> +asl::status_or<asl::parse_number_result<T>> parse_integer(asl::string_view sv, int base) +{ + ASL_ASSERT(base >= 2 && base <= 16); + + if (sv.is_empty()) { return asl::invalid_argument_error(); } + + T value = 0; + bool is_negative = false; + + if (asl::is_signed_integer<T> && sv[0] == '-') + { + is_negative = true; + sv = sv.substr(1); + } + + isize_t cursor = 0; + while (cursor < sv.size()) + { + // NOLINTNEXTLINE(*-array-index) + int8_t digit = kBase16Table[static_cast<uint8_t>(sv[cursor])]; + if (digit < 0 || digit >= base) { break; } + + if (__builtin_mul_overflow(value, static_cast<T>(base), &value)) + { + return asl::invalid_argument_error("overflow"); + } + + if (asl::is_signed_integer<T> && is_negative) + { + digit = static_cast<T>(-digit); + } + + if (__builtin_add_overflow(value, static_cast<T>(digit), &value)) + { + return asl::invalid_argument_error("overflow"); + } + + cursor += 1; + } + + if (cursor == 0) + { + return asl::invalid_argument_error(); + } + + return asl::parse_number_result<T>{ + .value = value, + .remaining = sv.substr(cursor), + }; +} + +} // anonymous namespace + +asl::status_or<asl::parse_number_result<uint8_t>> asl::parse_uint8(string_view sv, int base) +{ + return parse_integer<uint8_t>(sv, base); +} + +asl::status_or<asl::parse_number_result<uint16_t>> asl::parse_uint16(string_view sv, int base) +{ + return parse_integer<uint16_t>(sv, base); +} + +asl::status_or<asl::parse_number_result<uint32_t>> asl::parse_uint32(string_view sv, int base) +{ + return parse_integer<uint32_t>(sv, base); +} + +asl::status_or<asl::parse_number_result<uint64_t>> asl::parse_uint64(string_view sv, int base) +{ + return parse_integer<uint64_t>(sv, base); +} + +asl::status_or<asl::parse_number_result<int8_t>> asl::parse_int8(string_view sv, int base) +{ + return parse_integer<int8_t>(sv, base); +} + +asl::status_or<asl::parse_number_result<int16_t>> asl::parse_int16(string_view sv, int base) +{ + return parse_integer<int16_t>(sv, base); +} + +asl::status_or<asl::parse_number_result<int32_t>> asl::parse_int32(string_view sv, int base) +{ + return parse_integer<int32_t>(sv, base); +} + +asl::status_or<asl::parse_number_result<int64_t>> asl::parse_int64(string_view sv, int base) +{ + return parse_integer<int64_t>(sv, base); +} + diff --git a/asl/strings/parse_number.hpp b/asl/strings/parse_number.hpp new file mode 100644 index 0000000..fae0841 --- /dev/null +++ b/asl/strings/parse_number.hpp @@ -0,0 +1,34 @@ +// Copyright 2025 Steven Le Rouzic +// +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#include "asl/types/status_or.hpp" +#include "asl/strings/string_view.hpp" + +namespace asl +{ + +template<typename T> +struct parse_number_result +{ + T value; + string_view remaining; +}; + +status_or<parse_number_result<float>> parse_float(string_view); +status_or<parse_number_result<double>> parse_double(string_view); + +status_or<parse_number_result<uint8_t>> parse_uint8(string_view, int base = 10); +status_or<parse_number_result<uint16_t>> parse_uint16(string_view, int base = 10); +status_or<parse_number_result<uint32_t>> parse_uint32(string_view, int base = 10); +status_or<parse_number_result<uint64_t>> parse_uint64(string_view, int base = 10); + +status_or<parse_number_result<int8_t>> parse_int8(string_view, int base = 10); +status_or<parse_number_result<int16_t>> parse_int16(string_view, int base = 10); +status_or<parse_number_result<int32_t>> parse_int32(string_view, int base = 10); +status_or<parse_number_result<int64_t>> parse_int64(string_view, int base = 10); + +} // namespace asl + diff --git a/asl/strings/parse_number_float.cpp b/asl/strings/parse_number_float.cpp new file mode 100644 index 0000000..4568278 --- /dev/null +++ b/asl/strings/parse_number_float.cpp @@ -0,0 +1,30 @@ +// Copyright 2025 Steven Le Rouzic +// +// SPDX-License-Identifier: BSD-3-Clause + +#include <fast_float.h> + +// We need to isolate fast_float.h completely from asl +// because it conflicts with our redefinitions of things +// from the STL. In this case it's operator new, but there +// might be other conflicts. + +namespace asl +{ + +extern bool parse_float_impl(const char** begin, const char* end, float* value) +{ + auto res = fast_float::from_chars(*begin, end, *value); + *begin = res.ptr; + return res.ec == std::errc{}; +} + +extern bool parse_double_impl(const char** begin, const char* end, double* value) +{ + auto res = fast_float::from_chars(*begin, end, *value); + *begin = res.ptr; + return res.ec == std::errc{}; +} + +} // namespace asl + diff --git a/asl/strings/parse_number_integer_table.py b/asl/strings/parse_number_integer_table.py new file mode 100644 index 0000000..7db41fb --- /dev/null +++ b/asl/strings/parse_number_integer_table.py @@ -0,0 +1,24 @@ +a = ord('a') +f = ord('f') +A = ord('A') +F = ord('F') +n0 = ord('0') +n9 = ord('9') + +output = "" + +for i in range(0, 16): + for j in range(0, 16): + v = i * 16 + j + n = -1 + if v >= a and v <= f: + n = v - a + 10 + elif v >= A and v <= F: + n = v - A + 10 + elif v >= n0 and v <= n9: + n = v - n0 + output += f"{n:>2}, " + output += "\n" + +print(output) + diff --git a/asl/strings/parse_number_tests.cpp b/asl/strings/parse_number_tests.cpp new file mode 100644 index 0000000..df759e9 --- /dev/null +++ b/asl/strings/parse_number_tests.cpp @@ -0,0 +1,203 @@ +// Copyright 2025 Steven Le Rouzic +// +// SPDX-License-Identifier: BSD-3-Clause + +#include "asl/strings/parse_number.hpp" +#include "asl/testing/testing.hpp" + +// @Todo Once we have an equivalent of std::numeric_limits, +// properly compare floating point values in these tests. + +ASL_TEST(parse_float_error) +{ + const asl::string_view sv = "this is not a number lmao"; + auto res = asl::parse_float(sv); + ASL_TEST_EXPECT(!res.ok()); +} + +ASL_TEST(parse_float_empty) +{ + const asl::string_view sv = ""; + auto res = asl::parse_float(sv); + ASL_TEST_EXPECT(!res.ok()); +} + +ASL_TEST(parse_float_simple) +{ + const asl::string_view sv = "3.1415"; + auto res = asl::parse_float(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 3.1415F); + ASL_TEST_EXPECT(res.value().remaining.size() == 0); +} + +ASL_TEST(parse_float_integer) +{ + const asl::string_view sv = "31415"; + auto res = asl::parse_float(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 31415.0F); + ASL_TEST_EXPECT(res.value().remaining.size() == 0); +} + +ASL_TEST(parse_float_scientific) +{ + const asl::string_view sv = "314.15e-2"; + auto res = asl::parse_float(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 3.1415F); + ASL_TEST_EXPECT(res.value().remaining.size() == 0); +} + +ASL_TEST(parse_float_suffix) +{ + const asl::string_view sv = "3.1415 yoyoyo"; + auto res = asl::parse_float(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 3.1415F); + ASL_TEST_EXPECT(res.value().remaining == " yoyoyo"); +} + +ASL_TEST(parse_int) +{ + const asl::string_view sv = "926473"; + auto res = asl::parse_uint32(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 926473); + ASL_TEST_EXPECT(res.value().remaining.is_empty()); +} + +ASL_TEST(parse_int_negative) +{ + const asl::string_view sv = "-926473"; + auto res = asl::parse_int32(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == -926473); + ASL_TEST_EXPECT(res.value().remaining.is_empty()); +} + +ASL_TEST(parse_int_suffix) +{ + const asl::string_view sv = "926473 what's this then"; + auto res = asl::parse_uint32(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 926473); + ASL_TEST_EXPECT(res.value().remaining == " what's this then"); +} + +ASL_TEST(parse_uint_with_minus) +{ + const asl::string_view sv = "-926473"; + auto res = asl::parse_uint32(sv); + ASL_TEST_EXPECT(!res.ok()); +} + +ASL_TEST(parse_int_with_only_minus) +{ + const asl::string_view sv = "-@"; + auto res = asl::parse_int32(sv); + ASL_TEST_EXPECT(!res.ok()); +} + +ASL_TEST(parse_uint_invalid) +{ + const asl::string_view sv = "abcd"; + auto res = asl::parse_uint32(sv); + ASL_TEST_EXPECT(!res.ok()); +} + +ASL_TEST(parse_uint_empty) +{ + const asl::string_view sv = ""; + auto res = asl::parse_uint32(sv); + ASL_TEST_EXPECT(!res.ok()); +} + +ASL_TEST(parse_uint_overflow) +{ + ASL_TEST_EXPECT(!asl::parse_uint16("80000").ok()); + ASL_TEST_EXPECT(!asl::parse_uint16("65536").ok()); +} + +ASL_TEST(parse_uint16_max) +{ + const asl::string_view sv = "65535"; + auto res = asl::parse_uint16(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 65535); + ASL_TEST_EXPECT(res.value().remaining.is_empty()); +} + +ASL_TEST(parse_uint16_zero) +{ + const asl::string_view sv = "0"; + auto res = asl::parse_uint16(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 0); + ASL_TEST_EXPECT(res.value().remaining.is_empty()); +} + +ASL_TEST(parse_uint16_zeros) +{ + const asl::string_view sv = "00000"; + auto res = asl::parse_uint16(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 0); + ASL_TEST_EXPECT(res.value().remaining.is_empty()); +} + +ASL_TEST(parse_int_overflow) +{ + ASL_TEST_EXPECT(!asl::parse_int16("80000").ok()); + ASL_TEST_EXPECT(!asl::parse_int16("40000").ok()); + ASL_TEST_EXPECT(!asl::parse_int16("32768").ok()); + ASL_TEST_EXPECT(!asl::parse_int16("-80000").ok()); + ASL_TEST_EXPECT(!asl::parse_int16("-40000").ok()); + ASL_TEST_EXPECT(!asl::parse_int16("-32769").ok()); +} + +ASL_TEST(parse_int16_max) +{ + const asl::string_view sv = "32767"; + auto res = asl::parse_int16(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 32767); + ASL_TEST_EXPECT(res.value().remaining.is_empty()); +} + +ASL_TEST(parse_int16_min) +{ + const asl::string_view sv = "-32768"; + auto res = asl::parse_int16(sv); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == -32768); + ASL_TEST_EXPECT(res.value().remaining.is_empty()); +} + +ASL_TEST(parse_hex) +{ + const asl::string_view sv = "1000a"; + auto res = asl::parse_uint32(sv, 16); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 65546); + ASL_TEST_EXPECT(res.value().remaining.is_empty()); +} + +ASL_TEST(parse_bin) +{ + const asl::string_view sv = "101010"; + auto res = asl::parse_uint32(sv, 2); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 42); + ASL_TEST_EXPECT(res.value().remaining.is_empty()); +} + +ASL_TEST(parse_oct) +{ + const asl::string_view sv = "644"; + auto res = asl::parse_uint32(sv, 8); + ASL_TEST_EXPECT(res.ok()); + ASL_TEST_EXPECT(res.value().value == 6 * 64 + 4 * 8 + 4); + ASL_TEST_EXPECT(res.value().remaining.is_empty()); +} + diff --git a/asl/strings/string_view.hpp b/asl/strings/string_view.hpp index 287b88f..d614512 100644 --- a/asl/strings/string_view.hpp +++ b/asl/strings/string_view.hpp @@ -28,6 +28,13 @@ public: , m_size{size} {} + constexpr string_view(const char* begin, const char* end) + : m_data{begin} + , m_size{end - begin} + { + ASL_ASSERT(begin <= end); + } + template<isize_t kSize> constexpr string_view(const char (&str)[kSize]) // NOLINT(*explicit*) requires (kSize >= 1) diff --git a/vendor/dragonbox/BUILD.bazel b/vendor/dragonbox/BUILD.bazel index 5028819..419e56c 100644 --- a/vendor/dragonbox/BUILD.bazel +++ b/vendor/dragonbox/BUILD.bazel @@ -18,7 +18,7 @@ cc_library( name = "dragonbox", hdrs = ["dragonbox.h"], includes = ["."], - visibility = ["//:__subpackages__"], + visibility = ["//asl:__subpackages__"], applicable_licenses = [ ":license", ], diff --git a/vendor/fast_float/BUILD.bazel b/vendor/fast_float/BUILD.bazel new file mode 100644 index 0000000..3dfec78 --- /dev/null +++ b/vendor/fast_float/BUILD.bazel @@ -0,0 +1,25 @@ +# Copyright 2025 Steven Le Rouzic +# +# SPDX-License-Identifier: BSD-3-Clause + +load("@rules_license//rules:license.bzl", "license") + +license( + name = "license", + license_kinds = [ + "@rules_license//licenses/spdx:MIT", + ], + license_text = "LICENSE.txt", + package_name = "fast_float", + package_url = "https://github.com/fastfloat/fast_float", +) + +cc_library( + name = "fast_float", + hdrs = ["fast_float.h"], + includes = ["."], + visibility = ["//asl:__subpackages__"], + applicable_licenses = [ + ":license", + ], +) diff --git a/vendor/fast_float/LICENSE.txt b/vendor/fast_float/LICENSE.txt new file mode 100644 index 0000000..2fb2a37 --- /dev/null +++ b/vendor/fast_float/LICENSE.txt @@ -0,0 +1,27 @@ +MIT License + +Copyright (c) 2021 The fast_float authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/fast_float/fast_float.h b/vendor/fast_float/fast_float.h new file mode 100644 index 0000000..cb044c2 --- /dev/null +++ b/vendor/fast_float/fast_float.h @@ -0,0 +1,4443 @@ +// fast_float by Daniel Lemire +// fast_float by João Paulo Magalhaes +// +// +// with contributions from Eugene Golushkov +// with contributions from Maksim Kita +// with contributions from Marcin Wojdyr +// with contributions from Neal Richardson +// with contributions from Tim Paine +// with contributions from Fabio Pellacini +// with contributions from Lénárd Szolnoki +// with contributions from Jan Pharago +// with contributions from Maya Warrier +// with contributions from Taha Khokhar +// with contributions from Anders Dalvander +// +// +// Licensed under the Apache License, Version 2.0, or the +// MIT License or the Boost License. This file may not be copied, +// modified, or distributed except according to those terms. +// +// MIT License Notice +// +// MIT License +// +// Copyright (c) 2021 The fast_float authors +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Apache License (Version 2.0) Notice +// +// Copyright 2021 The fast_float authors +// Licensed 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 +// +// BOOST License Notice +// +// Boost Software License - Version 1.0 - August 17th, 2003 +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +#ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H +#define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H + +#ifdef __has_include +#if __has_include(<version>) +#include <version> +#endif +#endif + +// Testing for https://wg21.link/N3652, adopted in C++14 +#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304 +#define FASTFLOAT_CONSTEXPR14 constexpr +#else +#define FASTFLOAT_CONSTEXPR14 +#endif + +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L +#define FASTFLOAT_HAS_BIT_CAST 1 +#else +#define FASTFLOAT_HAS_BIT_CAST 0 +#endif + +#if defined(__cpp_lib_is_constant_evaluated) && \ + __cpp_lib_is_constant_evaluated >= 201811L +#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1 +#else +#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0 +#endif + +#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L +#define FASTFLOAT_IF_CONSTEXPR17(x) if constexpr (x) +#else +#define FASTFLOAT_IF_CONSTEXPR17(x) if (x) +#endif + +// Testing for relevant C++20 constexpr library features +#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \ + defined(__cpp_lib_constexpr_algorithms) && \ + __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/ +#define FASTFLOAT_CONSTEXPR20 constexpr +#define FASTFLOAT_IS_CONSTEXPR 1 +#else +#define FASTFLOAT_CONSTEXPR20 +#define FASTFLOAT_IS_CONSTEXPR 0 +#endif + +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 0 +#else +#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1 +#endif + +#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H + +#ifndef FASTFLOAT_FLOAT_COMMON_H +#define FASTFLOAT_FLOAT_COMMON_H + +#include <cfloat> +#include <cstdint> +#include <cassert> +#include <cstring> +#include <limits> +#include <type_traits> +#include <system_error> +#ifdef __has_include +#if __has_include(<stdfloat>) && (__cplusplus > 202002L || (defined(_MSVC_LANG) && (_MSVC_LANG > 202002L))) +#include <stdfloat> +#endif +#endif + +#define FASTFLOAT_VERSION_MAJOR 8 +#define FASTFLOAT_VERSION_MINOR 0 +#define FASTFLOAT_VERSION_PATCH 2 + +#define FASTFLOAT_STRINGIZE_IMPL(x) #x +#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x) + +#define FASTFLOAT_VERSION_STR \ + FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MAJOR) \ + "." FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MINOR) "." FASTFLOAT_STRINGIZE( \ + FASTFLOAT_VERSION_PATCH) + +#define FASTFLOAT_VERSION \ + (FASTFLOAT_VERSION_MAJOR * 10000 + FASTFLOAT_VERSION_MINOR * 100 + \ + FASTFLOAT_VERSION_PATCH) + +namespace fast_float { + +enum class chars_format : uint64_t; + +namespace detail { +constexpr chars_format basic_json_fmt = chars_format(1 << 5); +constexpr chars_format basic_fortran_fmt = chars_format(1 << 6); +} // namespace detail + +enum class chars_format : uint64_t { + scientific = 1 << 0, + fixed = 1 << 2, + hex = 1 << 3, + no_infnan = 1 << 4, + // RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6 + json = uint64_t(detail::basic_json_fmt) | fixed | scientific | no_infnan, + // Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed. + json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific, + fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific, + general = fixed | scientific, + allow_leading_plus = 1 << 7, + skip_white_space = 1 << 8, +}; + +template <typename UC> struct from_chars_result_t { + UC const *ptr; + std::errc ec; +}; + +using from_chars_result = from_chars_result_t<char>; + +template <typename UC> struct parse_options_t { + constexpr explicit parse_options_t(chars_format fmt = chars_format::general, + UC dot = UC('.'), int b = 10) + : format(fmt), decimal_point(dot), base(b) {} + + /** Which number formats are accepted */ + chars_format format; + /** The character used as decimal point */ + UC decimal_point; + /** The base used for integers */ + int base; +}; + +using parse_options = parse_options_t<char>; + +} // namespace fast_float + +#if FASTFLOAT_HAS_BIT_CAST +#include <bit> +#endif + +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ + defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) || \ + defined(__MINGW64__) || defined(__s390x__) || \ + (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \ + defined(__PPC64LE__)) || \ + defined(__loongarch64)) +#define FASTFLOAT_64BIT 1 +#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(__arm__) || defined(_M_ARM) || defined(__ppc__) || \ + defined(__MINGW32__) || defined(__EMSCRIPTEN__)) +#define FASTFLOAT_32BIT 1 +#else + // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. +// We can never tell the register width, but the SIZE_MAX is a good +// approximation. UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max +// portability. +#if SIZE_MAX == 0xffff +#error Unknown platform (16-bit, unsupported) +#elif SIZE_MAX == 0xffffffff +#define FASTFLOAT_32BIT 1 +#elif SIZE_MAX == 0xffffffffffffffff +#define FASTFLOAT_64BIT 1 +#else +#error Unknown platform (not 32-bit, not 64-bit?) +#endif +#endif + +#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) || \ + (defined(_M_ARM64) && !defined(__MINGW32__)) +#include <intrin.h> +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define FASTFLOAT_VISUAL_STUDIO 1 +#endif + +#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ +#define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#elif defined _WIN32 +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#if defined(__APPLE__) || defined(__FreeBSD__) +#include <machine/endian.h> +#elif defined(sun) || defined(__sun) +#include <sys/byteorder.h> +#elif defined(__MVS__) +#include <sys/endian.h> +#else +#ifdef __has_include +#if __has_include(<endian.h>) +#include <endian.h> +#endif //__has_include(<endian.h>) +#endif //__has_include +#endif +# +#ifndef __BYTE_ORDER__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#ifndef __ORDER_LITTLE_ENDIAN__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#define FASTFLOAT_IS_BIG_ENDIAN 1 +#endif +#endif + +#if defined(__SSE2__) || (defined(FASTFLOAT_VISUAL_STUDIO) && \ + (defined(_M_AMD64) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP == 2))) +#define FASTFLOAT_SSE2 1 +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#define FASTFLOAT_NEON 1 +#endif + +#if defined(FASTFLOAT_SSE2) || defined(FASTFLOAT_NEON) +#define FASTFLOAT_HAS_SIMD 1 +#endif + +#if defined(__GNUC__) +// disable -Wcast-align=strict (GCC only) +#define FASTFLOAT_SIMD_DISABLE_WARNINGS \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wcast-align\"") +#else +#define FASTFLOAT_SIMD_DISABLE_WARNINGS +#endif + +#if defined(__GNUC__) +#define FASTFLOAT_SIMD_RESTORE_WARNINGS _Pragma("GCC diagnostic pop") +#else +#define FASTFLOAT_SIMD_RESTORE_WARNINGS +#endif + +#ifdef FASTFLOAT_VISUAL_STUDIO +#define fastfloat_really_inline __forceinline +#else +#define fastfloat_really_inline inline __attribute__((always_inline)) +#endif + +#ifndef FASTFLOAT_ASSERT +#define FASTFLOAT_ASSERT(x) \ + { ((void)(x)); } +#endif + +#ifndef FASTFLOAT_DEBUG_ASSERT +#define FASTFLOAT_DEBUG_ASSERT(x) \ + { ((void)(x)); } +#endif + +// rust style `try!()` macro, or `?` operator +#define FASTFLOAT_TRY(x) \ + { \ + if (!(x)) \ + return false; \ + } + +#define FASTFLOAT_ENABLE_IF(...) \ + typename std::enable_if<(__VA_ARGS__), int>::type + +namespace fast_float { + +fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() { +#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED + return std::is_constant_evaluated(); +#else + return false; +#endif +} + +template <typename T> +struct is_supported_float_type + : std::integral_constant< + bool, std::is_same<T, double>::value || std::is_same<T, float>::value +#ifdef __STDCPP_FLOAT64_T__ + || std::is_same<T, std::float64_t>::value +#endif +#ifdef __STDCPP_FLOAT32_T__ + || std::is_same<T, std::float32_t>::value +#endif +#ifdef __STDCPP_FLOAT16_T__ + || std::is_same<T, std::float16_t>::value +#endif +#ifdef __STDCPP_BFLOAT16_T__ + || std::is_same<T, std::bfloat16_t>::value +#endif + > { +}; + +template <typename T> +using equiv_uint_t = typename std::conditional< + sizeof(T) == 1, uint8_t, + typename std::conditional< |