summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-tidy1
-rw-r--r--asl/strings/BUILD.bazel20
-rw-r--r--asl/strings/parse_number.cpp162
-rw-r--r--asl/strings/parse_number.hpp34
-rw-r--r--asl/strings/parse_number_float.cpp30
-rw-r--r--asl/strings/parse_number_integer_table.py24
-rw-r--r--asl/strings/parse_number_tests.cpp203
-rw-r--r--asl/strings/string_view.hpp7
-rw-r--r--vendor/dragonbox/BUILD.bazel2
-rw-r--r--vendor/fast_float/BUILD.bazel25
-rw-r--r--vendor/fast_float/LICENSE.txt27
-rw-r--r--vendor/fast_float/fast_float.h4443
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