diff --git a/.cirrus.yml b/.cirrus.yml
deleted file mode 100644
index 0aff9456..00000000
--- a/.cirrus.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-build_and_store_wheels: &BUILD_AND_STORE_WHEELS
- install_cibuildwheel_script:
- - python -m pip install cibuildwheel==2.16.2
- run_cibuildwheel_script:
- - cibuildwheel
- wheels_artifacts:
- path: "wheelhouse/*"
-
- # Upload only for tagged commit
- only_if: $CIRRUS_TAG != ''
- publish_script:
- - python -m pip install twine
- - python -m twine upload --repository-url https://upload.pypi.org/legacy/ --username __token__ wheelhouse/*.whl
-
-
-linux_aarch64_task:
- name: Build Linux aarch64 wheels.
- compute_engine_instance:
- image_project: cirrus-images
- image: family/docker-builder-arm64
- architecture: arm64
- platform: linux
- cpu: 4
- memory: 4G
- environment:
- TWINE_PASSWORD: ENCRYPTED[ade2037764e68fea251152f7585f3f77cdd748af06dc0f06942c45a8a8770fff19032c985f8dc193229c8adb2c0fecb9]
-
- install_pre_requirements_script:
- - apt install -y python3-venv python-is-python3
- <<: *BUILD_AND_STORE_WHEELS
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..0fd988d9
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,15 @@
+# These are supported funding model platforms
+
+github: syoyo # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+polar: # Replace with a single Polar username
+buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
+thanks_dev: # Replace with a single thanks.dev username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml
new file mode 100644
index 00000000..77d39d3c
--- /dev/null
+++ b/.github/workflows/cron.yml
@@ -0,0 +1,22 @@
+name: Close inactive issues
+on:
+ schedule:
+ - cron: "30 1 * * *"
+
+jobs:
+ close-issues:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ steps:
+ - uses: actions/stale@v9
+ with:
+ days-before-issue-stale: 14
+ days-before-issue-close: 14
+ stale-issue-label: "stale"
+ stale-issue-message: "This issue is stale because it has been open for 14 days with no activity."
+ close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
+ days-before-pr-stale: -1
+ days-before-pr-close: -1
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml
new file mode 100644
index 00000000..b40cf7a0
--- /dev/null
+++ b/.github/workflows/python.yml
@@ -0,0 +1,196 @@
+name: Python
+
+on: [push, pull_request]
+
+jobs:
+ check_format:
+ name: Check Python code format
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install uv
+ uses: astral-sh/setup-uv@v4
+ with:
+ version: "latest"
+
+ - name: Set up Python and install dependencies
+ working-directory: tests/python
+ run: uv sync --project . --python 3.13
+
+ - name: Check code format
+ working-directory: tests/python
+ run: uv run --project . black --check ../..
+
+ build_wheels_quick:
+ name: Build wheels for quick testing
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ fetch-tags: true # Optional, use if you use setuptools_scm
+
+ - name: Build wheels
+ uses: pypa/cibuildwheel@v3.3.1
+ env:
+ # These are the only wheels we need for the `test_wheels` job. For
+ # faster iteration times, we limit the job to only build these wheels.
+ # Restrict to CPython to avoid building unused PyPy wheels.
+ CIBW_BUILD: "cp*-manylinux_x86_64"
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: cibw-wheels-quick
+ path: ./wheelhouse/*.whl
+
+ test_wheels:
+ name: Test wheels with Python ${{ matrix.python-version }} and NumPy ${{ matrix.numpy-version }}
+ needs: [build_wheels_quick]
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - python-version: "3.9"
+ numpy-version: "1.25.2"
+ - python-version: "3.10"
+ numpy-version: "1.26.4"
+ - python-version: "3.11"
+ numpy-version: "1.26.4"
+ - python-version: "3.12"
+ numpy-version: "1.26.4"
+ - python-version: "3.11"
+ numpy-version: "2.4.2"
+ - python-version: "3.12"
+ numpy-version: "2.4.2"
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install uv
+ uses: astral-sh/setup-uv@v4
+ with:
+ version: "latest"
+
+ - name: Download wheel artifacts
+ uses: actions/download-artifact@v4
+ with:
+ pattern: cibw-wheels-quick
+ path: dist
+ merge-multiple: true
+
+ - name: Set up Python ${{ matrix.python-version }} and install dependencies
+ working-directory: tests/python
+ run: uv sync --project . --python ${{ matrix.python-version }}
+
+ - name: Install NumPy ${{ matrix.numpy-version }}
+ working-directory: tests/python
+ run: |
+ uv pip install --project . --only-binary :all: numpy==${{ matrix.numpy-version }}
+
+ - name: Install manylinux wheel built for Python ${{ matrix.python-version }}
+ working-directory: tests/python
+ run: uv pip install --project . ../../dist/*cp$(echo ${{ matrix.python-version }} | tr -d .)*.whl
+
+ - name: Run tests
+ working-directory: tests/python
+ run: uv run --project . pytest
+
+ build_wheels_main:
+ name: Build remaining wheels on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest, windows-latest, macos-latest]
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ fetch-tags: true # Optional, use if you use setuptools_scm
+
+ - name: Build wheels
+ uses: pypa/cibuildwheel@v3.3.1
+ env:
+ CIBW_ARCHS_MACOS: "x86_64 universal2 arm64"
+ CIBW_ARCHS_WINDOWS: "AMD64"
+ # The quick build has already taken care of manylinux.
+ CIBW_SKIP: "*-manylinux_x86_64"
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: cibw-wheels-main-${{ matrix.os }}-${{ strategy.job-index }}
+ path: ./wheelhouse/*.whl
+
+ # It looks cibuildwheels did not clean build folder(CMake), and it results to Windows arm64 build failure(trying to reuse x86 build of .obj)
+ # So supply separated build job for Windows ARM64 build
+ # TODO: clean build folder using CIBW_BEFORE_ALL?
+ build_wheels_win_arm64:
+ name: Build ARM64 wheels on Windows
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ fetch-tags: true # Optional, use if you use setuptools_scm
+
+ - name: Build wheels
+ uses: pypa/cibuildwheel@v3.3.1
+ env:
+ CIBW_ARCHS_WINDOWS: "ARM64"
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: cibw-wheels-win-arm64
+ path: ./wheelhouse/*.whl
+
+ make_sdist:
+ name: Make SDist
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # Optional, use if you use setuptools_scm
+ fetch-tags: true # Optional, use if you use setuptools_scm
+
+ - name: Build SDist
+ run: pipx run build --sdist
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: cibw-sdist
+ path: dist/*.tar.gz
+
+ upload_all:
+ needs: [build_wheels_quick, build_wheels_main, build_wheels_win_arm64, make_sdist]
+ runs-on: ubuntu-latest
+ environment: release
+ permissions:
+ # IMPORTANT: this permission is mandatory for trusted publishing
+ id-token: write
+ # upload to PyPI on every tag starting with 'v'
+ # NOTE: Without github.event_name & githug.ref check, `upload_all` task is still triggered on 'main' branch push.
+ # (then get 'Branch "main" is not allowed to deploy to release due to environment protection rules.' error)
+ # So still do event_name and github.ref check.
+ # TODO: Make it work only using Github `environment` feature.
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
+ # alternatively, to publish when a GitHub Release is created, use the following rule:
+ # if: github.event_name == 'push' && github.event.action == 'published'
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ pattern: cibw-*
+ path: dist
+ merge-multiple: true
+
+ - uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ # Use Trusted Publisher feature:
+ # https://docs.pypi.org/trusted-publishers/
+ # so no use of PYPI_API_TOKEN
+ #password: ${{ secrets.PYPI_API_TOKEN }}
+ #
+ # Avoid race condition when using multiple CIs
+ skip-existing: true
+ verbose: true
diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml
new file mode 100644
index 00000000..3a7ffc82
--- /dev/null
+++ b/.github/workflows/unit.yml
@@ -0,0 +1,22 @@
+name: Unit Tests
+
+on:
+ push:
+ branches:
+ - '**'
+ tags:
+ - 'v*'
+ pull_request:
+ branches:
+ - '**'
+
+jobs:
+ unit_linux:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - name: Run unit tests
+ run: |
+ cd tests
+ make check
diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml
deleted file mode 100644
index b37104a2..00000000
--- a/.github/workflows/wheels.yml
+++ /dev/null
@@ -1,115 +0,0 @@
-name: Build and upload to PyPI
-
-# Build on every branch push, tag push, and pull request change:
-on: [push, pull_request]
-
-jobs:
-
- build_wheels:
- name: Build wheels on ${{ matrix.os }}
- runs-on: ${{ matrix.os }}
- strategy:
- matrix:
- os: [ubuntu-latest, windows-latest, macos-latest]
-
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
- fetch-tags: true # Optional, use if you use setuptools_scm
-
- - name: Build wheels
- uses: pypa/cibuildwheel@v2.16.5
- # to supply options, put them in 'env', like:
- # env:
- # CIBW_SOME_OPTION: value
- # Disable building PyPy wheels on all platforms
- env:
- CIBW_ARCHS_MACOS: "x86_64 universal2 arm64"
- CIBW_ARCHS_WINDOWS: "AMD64 x86"
- # disable aarm64 build since its too slow to build(docker + qemu)
- CIBW_ARCHS_LINUX: "x86_64 i686"
- # it looks cibuildwheel fails to add version string to wheel file for python 3.6, so skip it
- CIBW_SKIP: pp*
-
- - uses: actions/upload-artifact@v4
- with:
- name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
- path: ./wheelhouse/*.whl
-
- # It looks cibuildwheels did not clean build folder(CMake), and it results to Windows arm64 build failure(trying to reuse x86 build of .obj)
- # So supply separated build job for Windows ARM64 build
- # TODO: clean build folder using CIBW_BEFORE_ALL?
- build_win_arm64_wheels:
- name: Build ARM64 wheels on Windows.
- runs-on: windows-latest
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
- fetch-tags: true # Optional, use if you use setuptools_scm
-
- - name: Build wheels
- uses: pypa/cibuildwheel@v2.16.5
- # to supply options, put them in 'env', like:
- # env:
- # CIBW_SOME_OPTION: value
- # Disable building PyPy wheels on all platforms
- env:
- CIBW_ARCHS_WINDOWS: "ARM64"
- CIBW_SKIP: pp*
-
- - uses: actions/upload-artifact@v4
- with:
- name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
- path: ./wheelhouse/*.whl
-
- make_sdist:
- name: Make SDist
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0 # Optional, use if you use setuptools_scm
- fetch-tags: true # Optional, use if you use setuptools_scm
-
- - name: Build SDist
- run: pipx run build --sdist
-
- - uses: actions/upload-artifact@v4
- with:
- name: cibw-sdist
- path: dist/*.tar.gz
-
- upload_all:
- needs: [build_wheels, build_wheels, make_sdist]
- runs-on: ubuntu-latest
- environment: release
- permissions:
- # IMPORTANT: this permission is mandatory for trusted publishing
- id-token: write
- # upload to PyPI on every tag starting with 'v'
- # NOTE: Without github.event_name & githug.ref check, `upload_all` task is still triggered on 'main' branch push.
- # (then get 'Branch "main" is not allowed to deploy to release due to environment protection rules.' error)
- # So still do event_name and github.ref check.
- # TODO: Make it work only using Github `environment` feature.
- if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
- # alternatively, to publish when a GitHub Release is created, use the following rule:
- # if: github.event_name == 'push' && github.event.action == 'published'
- steps:
- - uses: actions/download-artifact@v4
- with:
- pattern: cibw-*
- path: dist
- merge-multiple: true
-
- - uses: pypa/gh-action-pypi-publish@release/v1
- with:
- # Use Trusted Publisher feature:
- # https://docs.pypi.org/trusted-publishers/
- # so no use of PYPI_API_TOKEN
- #password: ${{ secrets.PYPI_API_TOKEN }}
- #
- # Avoid race condition when using multiple CIs
- skip-existing: true
- verbose: true
diff --git a/.gitignore b/.gitignore
index f9b4d691..4bd2eb9d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,10 @@ build/
/python/tiny_obj_loader.h
/tests/tester
/tests/tester.dSYM
+/_codeql_build_dir/
+/_codeql_detected_source_root
+/python/_version.py
+/tinyobjloader.egg-info/
+**/__pycache__/
+
+/Testing/Temporary/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 12b67f28..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-language: cpp
-sudo: required
-matrix:
- include:
- - addons: &1
- apt:
- sources:
- - george-edison55-precise-backports
- - ubuntu-toolchain-r-test
- - llvm-toolchain-precise-3.7
- packages:
- - cmake
- - cmake-data
- - ninja-build
- - g++-4.9
- - clang-3.7
- compiler: clang
- env: DEPLOY_BUILD=1 COMPILER_VERSION=3.7 BUILD_TYPE=Debug
- - addons: *1
- compiler: clang
- env: COMPILER_VERSION=3.7 BUILD_TYPE=Release
- - addons: &2
- apt:
- sources:
- - george-edison55-precise-backports
- - ubuntu-toolchain-r-test
- packages:
- - cmake
- - cmake-data
- - ninja-build
- - g++-4.9
- compiler: gcc
- env: COMPILER_VERSION=4.9 BUILD_TYPE=Debug
- - addons: *2
- compiler: gcc
- env: COMPILER_VERSION=4.9 BUILD_TYPE=Release
- - addons: *1
- compiler: clang
- env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug
-before_install:
-- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi
-- if [ -n "$REPORT_COVERAGE" ]; then sudo apt-get update python; fi
-- if [ -n "$REPORT_COVERAGE" ]; then sudo apt-get install python-dev libffi-dev libssl-dev; fi
-- if [ -n "$REPORT_COVERAGE" ]; then sudo pip install --upgrade pip; fi
-- if [ -n "$REPORT_COVERAGE" ]; then CXX=g++ pip install --user requests[security]; fi
-- if [ -n "$REPORT_COVERAGE" ]; then CXX=g++ pip install --user cpp-coveralls; fi
-script:
-- cd tests
-- make check
-- if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r .. -e examples -e tools -e
- jni -e python -e images -E ".*CompilerId.*" -E ".*feature_tests.*" ; fi
-- cd ..
-- rm -rf dist
-- mkdir dist
-- cp tiny_obj_loader.h dist/
-
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..87715260
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+# Use this for strict compilation check(will work on clang 3.8+)
+#EXTRA_CXXFLAGS := -fsanitize=address,undefined -Wall -Werror -Weverything -DTINYOBJLOADER_ENABLE_THREADED=1 -Wno-c++98-compat
+#EXTRA_CXXFLAGS := -Weverything -Wall -DTINYOBJLOADER_ENABLE_THREADED=1 -Wno-c++98-compat
+# Note: fast_float is now bundled by default (no separate include path needed).
+# Define TINYOBJLOADER_DISABLE_FAST_FLOAT to opt out of the bundled parser.
+EXTRA_CXXFLAGS :=
+
+all:
+ g++ $(EXTRA_CXXFLAGS) -DTINYOBJLOADER_USE_DOUBLE=1 -std=c++11 -g -O0 -o loader_example loader_example.cc
+ #clang++ $(EXTRA_CXXFLAGS) -DTINYOBJLOADER_USE_DOUBLE=1 -std=c++11 -g -O2 -o loader_example loader_example.cc
+
+lint:
+ ./cpplint.py tiny_gltf_loader.h
diff --git a/README.md b/README.md
index 2bf40db1..0150156a 100644
--- a/README.md
+++ b/README.md
@@ -2,24 +2,16 @@
[](https://badge.fury.io/py/tinyobjloader)
-[](https://dev.azure.com/tinyobjloader/tinyobjloader/_build/latest?definitionId=1&branchName=master)
-
-[](https://ci.appveyor.com/project/syoyo/tinyobjloader-6e4qf/branch/master)
-
-[](https://coveralls.io/github/syoyo/tinyobjloader?branch=master)
-
-[](https://aur.archlinux.org/packages/tinyobjloader)
-
-Tiny but powerful single file wavefront obj loader written in C++03. No dependency except for C++ STL. It can parse over 10M polygons with moderate memory and time.
+Tiny but powerful single file wavefront obj loader written in C++11. No dependency except for C++ STL. It can parse over 10M polygons with moderate memory and time.
`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-)
-If you are looking for C89 version, please see https://github.com/syoyo/tinyobjloader-c .
+If you are looking for C99 version, please see https://github.com/syoyo/tinyobjloader-c .
Version notice
--------------
-We recommend to use `master`(`main`) branch. Its v2.0 release candidate. Most features are now nearly robust and stable(Remaining task for release v2.0 is polishing C++ and Python API, and fix built-in triangulation code).
+We recommend using the `release` (main) branch. It contains the v2.0 release candidate. Most features are now nearly robust and stable. (The remaining task for release v2.0 is polishing C++ and Python API, and fix built-in triangulation code).
We have released new version v1.0.0 on 20 Aug, 2016.
Old version is available as `v0.9.x` branch https://github.com/syoyo/tinyobjloader/tree/v0.9.x
@@ -34,7 +26,7 @@ Old version is available as `v0.9.x` branch https://github.com/syoyo/tinyobjload
## Requirements
-* C++03 compiler
+* C++11 compiler
### Old version
@@ -74,8 +66,10 @@ TinyObjLoader is successfully used in ...
* metal-ray-tracer - Writing ray-tracer using Metal Performance Shaders https://github.com/sergeyreznik/metal-ray-tracer https://sergeyreznik.github.io/metal-ray-tracer/index.html
* Supernova Engine - 2D and 3D projects with Lua or C++ in data oriented design: https://github.com/supernovaengine/supernova
* AGE (Arc Game Engine) - An open-source engine for building 2D & 3D real-time rendering and interactive contents: https://github.com/MohitSethi99/ArcGameEngine
-* [Wicked Engine
](https://github.com/turanszkij/WickedEngine) - 3D engine with modern graphics
-* Your project here! (Letting us know via github issue is welcome!)
+* [Wicked Engine
](https://github.com/turanszkij/WickedEngine) - 3D engine with modern graphics
+* [Lumina Game Engine](https://github.com/MrDrElliot/LuminaEngine) - A modern, high-performance game engine built with Vulkan
+* lacecore: Python polygonal mesh library optimized for cloud computation https://github.com/lace/lacecore
+* Your project here! (Plese send PR)
### Old version(v0.9.x)
@@ -245,12 +239,28 @@ TinyObjLoader now use `real_t` for floating point data type.
Default is `float(32bit)`.
You can enable `double(64bit)` precision by using `TINYOBJLOADER_USE_DOUBLE` define.
+### High-performance float parsing (fast_float)
+
+By default, TinyObjLoader embeds [fast_float v8.0.2](https://github.com/fastfloat/fast_float)
+for ~3× faster, bit-exact ASCII-to-float conversion (equivalent to `strtod` but without locale overhead).
+
+To opt out and use the built-in hand-written parser instead, define:
+
+```c++
+#define TINYOBJLOADER_DISABLE_FAST_FLOAT
+#define TINYOBJLOADER_IMPLEMENTATION
+#include "tiny_obj_loader.h"
+```
+
+**Note:** If your project already includes `fast_float` under the `fast_float` namespace,
+defining `TINYOBJLOADER_DISABLE_FAST_FLOAT` avoids a redefinition conflict.
+
### Robust triangulation
When you enable `triangulation`(default is enabled),
TinyObjLoader triangulate polygons(faces with 4 or more vertices).
-Built-in trinagulation code may not work well in some polygon shape.
+Built-in triangulation code may not work well in some polygon shape.
You can define `TINYOBJLOADER_USE_MAPBOX_EARCUT` for robust triangulation using `mapbox/earcut.hpp`.
This requires C++11 compiler though. And you need to copy `mapbox/earcut.hpp` to your project.
@@ -260,7 +270,7 @@ If you have your own `mapbox/earcut.hpp` file incuded in your project, you can d
```c++
#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc
-// Optional. define TINYOBJLOADER_USE_MAPBOX_EARCUT gives robust trinagulation. Requires C++11
+// Optional. define TINYOBJLOADER_USE_MAPBOX_EARCUT gives robust triangulation. Requires C++11
//#define TINYOBJLOADER_USE_MAPBOX_EARCUT
#include "tiny_obj_loader.h"
@@ -332,7 +342,7 @@ for (size_t s = 0; s < shapes.size(); s++) {
```c++
#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc
-// Optional. define TINYOBJLOADER_USE_MAPBOX_EARCUT gives robust trinagulation. Requires C++11
+// Optional. define TINYOBJLOADER_USE_MAPBOX_EARCUT gives robust triangulation. Requires C++11
//#define TINYOBJLOADER_USE_MAPBOX_EARCUT
#include "tiny_obj_loader.h"
@@ -425,17 +435,16 @@ See [python/sample.py](python/sample.py) for example use of Python binding of ti
### CI + PyPI upload
-cibuildwheels + twine upload for each git tagging event is handled in Github Actions and Cirrus CI(arm builds).
+cibuildwheels + twine upload for each git tagging event is handled in Github Actions.
#### How to bump version(For developer)
-* Apply `black` to python files(`python/sample.py`)
* Bump version in CMakeLists.txt
* Commit and push `release`. Confirm C.I. build is OK.
* Create tag starting with `v`(e.g. `v2.1.0`)
* `git push --tags`
* version settings is automatically handled in python binding through setuptools_scm.
- * cibuildwheels + pypi upload(through twine) will be automatically triggered in Github Actions + Cirrus CI.
+ * cibuildwheels + pypi upload (through twine) will be automatically triggered in Github Actions.
## Tests
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 93f691ce..00000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-version: 1.0.{build}
-
-platform: x64
-
-install:
- #######################################################################################
- # All external dependencies are installed in C:\projects\deps
- #######################################################################################
- - mkdir C:\projects\deps
-
- #######################################################################################
- # Install Ninja
- #######################################################################################
- - set NINJA_URL="https://github.com/ninja-build/ninja/releases/download/v1.10.0/ninja-win.zip"
- - appveyor DownloadFile %NINJA_URL% -FileName ninja.zip
- - 7z x ninja.zip -oC:\projects\deps\ninja > nul
- - set PATH=C:\projects\deps\ninja;%PATH%
- - ninja --version
-
-build_script:
- - cd tests
- - vcbuild.bat
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
deleted file mode 100644
index 2580f172..00000000
--- a/azure-pipelines.yml
+++ /dev/null
@@ -1,170 +0,0 @@
-#
-# Python wheels build is now done in Github Actions + Cirrus CI(for arm build)
-# so python build is disabled in Azure pipelines.
-#
-
-variables:
- # https://cibuildwheel.readthedocs.io/en/stable/cpp_standards/
- # cibuildwheel now supports python 3.6+(as of 2022 Oct)
- #CIBW_SKIP: "pp*"
- CIBW_BEFORE_BUILD: "pip install pybind11"
- CIBW_ARCHS_LINUXBEFORE_BUILD: "pip install pybind11"
- # disable aarch64 build for a while since it(pulling docker aarch64 image) exceeds Azure's 60 min limit
- # NOTE: aarch64 linux support in Azure pipeline is not yet officially supported(as of 2022 Oct) https://github.com/microsoft/azure-pipelines-agent/issues/3935
- #CIBW_ARCHS_LINUX: auto aarch64
- CIBW_ARCHS_MACOS: x86_64 universal2 arm64
- #CIBW_BEFORE_BUILD_MACOS: "pip install -U pip setuptools"
- #CIBW_BEFORE_BUILD_LINUX: "pip install -U pip setuptools"
- #CIBW_TEST_COMMAND: TODO "python -c \"import tinyobjloader; tinyobjloader.test()\""
- CIBW_BUILD_VERBOSITY: "2"
- #CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
- #CIBW_MANYLINUX_I686_IMAGE: manylinux2014
-
-jobs:
- - job: unit_linux
- pool: { vmImage: "ubuntu-latest" }
- steps:
- - script: |
- cd tests
- make && ./tester
- displayName: Run unit tests
-
- - job: python_format
- pool: { vmImage: "ubuntu-latest" }
- steps:
- - task: UsePythonVersion@0
- - script: |
- # 19.10b0 triggers 'cannot import name '_unicodefun' from 'click'' error.
- # https://stackoverflow.com/questions/71673404/importerror-cannot-import-name-unicodefun-from-click
- #pip install black==19.10b0
- #pip install black==22.3.0
- pip install black==22.10.0
-
- black --check python/
- displayName: Check Python code format
-
- # Disabled: python build
- ##
- ## Ubuntu16.04 seems now deprecated(as of 2021/12/01),
- ## so use `ubuntu-latest`
- #- job: linux
- # pool: {vmImage: "ubuntu-latest"}
- # steps:
- # - task: UsePythonVersion@0
- # - bash: |
- # python3 -m pip install --upgrade pip
- # pip3 install cibuildwheel twine
-
- # # Use pipx to build source dist
- # pip3 install pipx
-
- # # Source dist
- # pipx run build --sdist
- # ls -la dist/*
-
- # # build binary wheels
- # cibuildwheel --platform linux --output-dir wheelhouse .
-
- # - task: CopyFiles@2
- # inputs:
- # contents: 'wheelhouse/**'
- # targetFolder: $(Build.ArtifactStagingDirectory)
-
- # - task: CopyFiles@2
- # inputs:
- # contents: 'dist/**'
- # targetFolder: $(Build.ArtifactStagingDirectory)
-
- # - task: PublishBuildArtifacts@1
- # inputs:
- # path: $(Build.ArtifactStagingDirectory)
- # artifactName: tinyobjDeployLinux
-
- #- job: macos
- # pool: {vmImage: 'macOS-latest'}
- # variables:
- # # Support C++11: https://github.com/joerick/cibuildwheel/pull/156
- # MACOSX_DEPLOYMENT_TARGET: 10.9
- # steps:
- # - task: UsePythonVersion@0
- # - bash: |
- # python3 -m pip install --upgrade pip
- # pip3 install cibuildwheel
- # cibuildwheel --platform macos --output-dir wheelhouse .
- # - task: CopyFiles@2
- # inputs:
- # contents: 'wheelhouse/*.whl'
- # targetFolder: $(Build.ArtifactStagingDirectory)
- # - task: PublishBuildArtifacts@1
- # inputs:
- # path: $(Build.ArtifactStagingDirectory)
- # artifactName: tinyobjDeployMacOS
-
- #- job: windows
- # pool: {vmImage: 'windows-latest'}
- # steps:
- # - task: UsePythonVersion@0
- # - bash: |
- # python -m pip install --upgrade pip
- # pip install cibuildwheel
- # cibuildwheel --platform windows --output-dir wheelhouse .
- # - task: CopyFiles@2
- # inputs:
- # contents: 'wheelhouse/*.whl'
- # targetFolder: $(Build.ArtifactStagingDirectory)
- # - task: PublishBuildArtifacts@1
- # inputs:
- # path: $(Build.ArtifactStagingDirectory)
- # artifactName: tinyobjDeployWindows
-
- #- job: deployPyPI
- # # Based on vispy: https://github.com/vispy/vispy/blob/master/azure-pipelines.yml
- # pool: {vmImage: 'ubuntu-latest'}
- # condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
- # dependsOn:
- # - linux
- # - macos
- # - windows
- # steps:
- # - task: UsePythonVersion@0
-
- # # TODO(syoyo): Use buildType: specific to download multiple artifacts at once?
- # - task: DownloadBuildArtifacts@0
- # inputs:
- # artifactName: 'tinyobjDeployLinux'
- # downloadPath: $(Pipeline.Workspace)
-
- # - task: DownloadBuildArtifacts@0
- # inputs:
- # artifactName: 'tinyobjDeployMacOS'
- # downloadPath: $(Pipeline.Workspace)
-
- # - task: DownloadBuildArtifacts@0
- # inputs:
- # artifactName: 'tinyobjDeployWindows'
- # downloadPath: $(Pipeline.Workspace)
-
- # # Publish to PyPI through twine
- # - bash: |
- # cd $(Pipeline.Workspace)
- # find .
- # python -m pip install --upgrade pip
- # pip install twine
- # echo tinyobjDeployLinux/dist/*
- # echo tinyobjDeployLinux/wheelhouse/* tinyobjDeployMacOS/wheelhouse/* tinyobjDeployWindows/wheelhouse/*
- # twine upload -u "__token__" --skip-existing tinyobjDeployLinux/dist/* tinyobjDeployLinux/wheelhouse/* tinyobjDeployMacOS/wheelhouse/* tinyobjDeployWindows/wheelhouse/*
- # env:
- # TWINE_PASSWORD: $(pypiToken2)
-
-trigger:
- branches:
- include:
- - '*'
- tags:
- include:
- - 'v*'
-
-pr:
- branches:
- include:
- - "*"
diff --git a/examples/viewer/viewer.cc b/examples/viewer/viewer.cc
index 059e57fe..2e6bd952 100644
--- a/examples/viewer/viewer.cc
+++ b/examples/viewer/viewer.cc
@@ -152,6 +152,8 @@ bool mouseRightPressed;
float curr_quat[4];
float prev_quat[4];
float eye[3], lookat[3], up[3];
+float g_angleX = 0.0f; // in degree
+float g_angleY = 0.0f; // in degree
bool g_show_wire = true;
bool g_cull_face = false;
@@ -235,6 +237,29 @@ struct mat3 {
}
};
+struct mat4 {
+ float m[4][4];
+ mat4() {
+ m[0][0] = 1.0f;
+ m[0][1] = 0.0f;
+ m[0][2] = 0.0f;
+ m[0][3] = 0.0f;
+ m[1][0] = 0.0f;
+ m[1][1] = 1.0f;
+ m[1][2] = 0.0f;
+ m[1][3] = 0.0f;
+ m[2][0] = 0.0f;
+ m[2][1] = 0.0f;
+ m[2][2] = 1.0f;
+ m[2][3] = 0.0f;
+ m[3][0] = 0.0f;
+ m[3][1] = 0.0f;
+ m[3][2] = 0.0f;
+ m[3][3] = 1.0f;
+ }
+};
+
+
void matmul3x3(const mat3 &a, const mat3 &b, mat3 &dst) {
for (size_t i = 0; i < 3; i++) {
for (size_t j = 0; j < 3; j++) {
@@ -247,6 +272,18 @@ void matmul3x3(const mat3 &a, const mat3 &b, mat3 &dst) {
}
}
+void matmul4x4(const mat4 &a, const mat4 &b, mat4 &dst) {
+ for (size_t i = 0; i < 4; i++) {
+ for (size_t j = 0; j < 4; j++) {
+ float v = 0.0f;
+ for (size_t k = 0; k < 4; k++) {
+ v += a.m[i][k] * b.m[k][j];
+ }
+ dst.m[i][j] = v;
+ }
+ }
+}
+
void normalizeVector(vec3 &v) {
float len2 = v.v[0] * v.v[0] + v.v[1] * v.v[1] + v.v[2] * v.v[2];
if (len2 > 0.0f) {
@@ -258,12 +295,13 @@ void normalizeVector(vec3 &v) {
}
}
-// Maya-like turntable
+// Maya-like turntable
// Reference:
// https://gamedev.stackexchange.com/questions/204367/implementing-a-maya-like-orbit-camera-in-vulkan-opengl
//
// angleX, angleY = angle in degree.
-static void turntable(float angleX, float angleY, float center[3]) {
+// TODO: scale
+static void turntable(float angleX, float angleY, float center[3], float dst[4][4]) {
float pivot[3];
pivot[0] = center[0];
pivot[1] = center[1];
@@ -299,7 +337,7 @@ static void turntable(float angleX, float angleY, float center[3]) {
rotX.m[2][1] = -sinX;
rotX.m[2][2] = cosX;
-
+
}
@@ -1138,15 +1176,25 @@ int main(int argc, char** argv) {
GLfloat mat[4][4];
gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0],
up[1], up[2]);
+
+ float center[3];
+ center[0] = 0.5 * (bmax[0] + bmin[0]);
+ center[1] = 0.5 * (bmax[1] + bmin[1]);
+ center[2] = 0.5 * (bmax[2] + bmin[2]);
+ float rotm[4][4];
+ turntable(g_angleX, g_angleY, center, rotm);
+
build_rotmatrix(mat, curr_quat);
glMultMatrixf(&mat[0][0]);
// Fit to -1, 1
glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent);
+#if 0
// Centerize object.
glTranslatef(-0.5 * (bmax[0] + bmin[0]), -0.5 * (bmax[1] + bmin[1]),
-0.5 * (bmax[2] + bmin[2]));
+#endif
Draw(gDrawObjects, materials, textures);
diff --git a/fuzzer/runner.py b/fuzzer/runner.py
index 0c06d4ba..a647d3ce 100644
--- a/fuzzer/runner.py
+++ b/fuzzer/runner.py
@@ -2,10 +2,11 @@
import glob
import subprocess
+
def main():
for g in glob.glob("../tests/afl/id*"):
print(g)
-
+
cmd = ["../a.out", g]
proc = subprocess.Popen(cmd)
diff --git a/models/cube_w_BOM.mtl b/models/cube_w_BOM.mtl
new file mode 100644
index 00000000..96255b54
--- /dev/null
+++ b/models/cube_w_BOM.mtl
@@ -0,0 +1,24 @@
+newmtl white
+Ka 0 0 0
+Kd 1 1 1
+Ks 0 0 0
+
+newmtl red
+Ka 0 0 0
+Kd 1 0 0
+Ks 0 0 0
+
+newmtl green
+Ka 0 0 0
+Kd 0 1 0
+Ks 0 0 0
+
+newmtl blue
+Ka 0 0 0
+Kd 0 0 1
+Ks 0 0 0
+
+newmtl light
+Ka 20 20 20
+Kd 1 1 1
+Ks 0 0 0
diff --git a/models/cube_w_BOM.obj b/models/cube_w_BOM.obj
new file mode 100644
index 00000000..3c395f04
--- /dev/null
+++ b/models/cube_w_BOM.obj
@@ -0,0 +1,32 @@
+mtllib cube_w_BOM.mtl
+
+v 0.000000 2.000000 2.000000
+v 0.000000 0.000000 2.000000
+v 2.000000 0.000000 2.000000
+v 2.000000 2.000000 2.000000
+v 0.000000 2.000000 0.000000
+v 0.000000 0.000000 0.000000
+v 2.000000 0.000000 0.000000
+v 2.000000 2.000000 0.000000
+# 8 vertices
+
+g front cube
+usemtl white
+f 1 2 3 4
+# two white spaces between 'back' and 'cube'
+g back cube
+# expects white material
+f 8 7 6 5
+g right cube
+usemtl red
+f 4 3 7 8
+g top cube
+usemtl white
+f 5 1 4 8
+g left cube
+usemtl green
+f 5 6 2 1
+g bottom cube
+usemtl white
+f 2 6 7 3
+# 6 elements
diff --git a/models/issue-389-comment.obj b/models/issue-389-comment.obj
new file mode 100644
index 00000000..cf16d926
--- /dev/null
+++ b/models/issue-389-comment.obj
@@ -0,0 +1,44 @@
+g Part 1
+v 0.0576127 0.0488792 0.0423
+v 0.0576127 0.0488792 0
+v -0.0483158 0.0488792 0
+v -0.0483158 0.0488792 0.0423
+v -0.0483158 -0.0139454 0
+v -0.0483158 -0.0139454 0.0423
+v 0.0576127 -0.0139454 0
+v 0.0576127 -0.0139454 0.0423
+vn 0 1 0
+vn -1 0 0
+vn 0 -1 0
+vn 1 0 0
+vn 0 0 1
+vn 0 0 -1
+o mesh0
+f 1//1 2//1 3//1
+f 3//1 4//1 1//1
+o mesh1
+f 4//2 3//2 5//2
+f 5//2 6//2 4//2
+o mesh2
+f 6//3 5//3 7//3
+f 7//3 8//3 6//3
+o mesh3
+f 8//4 7//4 2//4
+f 2//4 1//4 8//4
+o mesh4
+f 8//5 1//5 4//5
+f 4//5 6//5 8//5
+o mesh5
+f 5//6 3//6 2//6
+f 2//6 7//6 5//6
+
+# Zusätzliche Linien (aus der Oberseite)
+o lines
+v 0.0576127 0.0488792 0.0423 # Startpunkt Linie 1 (Ecke 1 Oberseite)
+v 0.0576127 0.0488792 0.2423 # Endpunkt Linie 1 (2m Höhe)
+v -0.0483158 -0.0139454 0.0423 # Startpunkt Linie 2 (Ecke 6 Oberseite)
+v -0.0483158 -0.0139454 0.2423 # Endpunkt Linie 2 (2m Höhe)
+
+# Linien
+l 1 9 # Linie 1
+l 6 10 # Linie 2
diff --git a/models/issue-391.mtl b/models/issue-391.mtl
new file mode 100644
index 00000000..c23ced4b
--- /dev/null
+++ b/models/issue-391.mtl
@@ -0,0 +1,4 @@
+newmtl has_kd
+Kd 1 0 0
+newmtl has_map
+map_Kd test.png
\ No newline at end of file
diff --git a/models/issue-391.obj b/models/issue-391.obj
new file mode 100644
index 00000000..06d8774b
--- /dev/null
+++ b/models/issue-391.obj
@@ -0,0 +1,9 @@
+mtllib issue-391.mtl
+v 0 0 0
+v 1 0 0
+v 0 1 0
+vn 0 0 1
+usemtl has_map
+f 1//1 2//1 3//1
+usemtl has_kd
+f 1//1 2//1 3//1
\ No newline at end of file
diff --git a/models/issue-400-num-face-vertices.obj b/models/issue-400-num-face-vertices.obj
new file mode 100644
index 00000000..77e25b48
--- /dev/null
+++ b/models/issue-400-num-face-vertices.obj
@@ -0,0 +1,15 @@
+# Regression test model for issue #400 - numpy_num_face_vertices()
+# Mixed quad and triangle faces to verify correct uint type handling.
+# With the bug (unsigned char instead of unsigned int in the numpy binding),
+# numpy_num_face_vertices() returned all zeros for quad (4-vertex) faces.
+v 0.0 0.0 0.0
+v 1.0 0.0 0.0
+v 1.0 1.0 0.0
+v 0.0 1.0 0.0
+v 0.5 0.5 1.0
+# quad face (num_face_vertices = 4)
+f 1 2 3 4
+# triangle face (num_face_vertices = 3)
+f 1 2 5
+# triangle face (num_face_vertices = 3)
+f 2 3 5
diff --git a/models/numeric-edge-cases.obj b/models/numeric-edge-cases.obj
new file mode 100644
index 00000000..71d8c156
--- /dev/null
+++ b/models/numeric-edge-cases.obj
@@ -0,0 +1,64 @@
+# Numeric edge cases for fast_float migration testing
+# Each vertex exercises a different parsing path.
+
+# v0: basic integers and zero
+v 0 0 0
+
+# v1: simple decimals
+v 1.5 -2.25 3.125
+
+# v2: leading decimal dot (no integer part)
+v .5 -.75 .001
+
+# v3: trailing dot (no fractional part)
+v 1. -2. 100.
+
+# v4: scientific notation (lowercase e)
+v 1.5e2 -3.0e-4 7e10
+
+# v5: scientific notation (uppercase E)
+v 2.5E3 -1.0E-2 4E+5
+
+# v6: leading + sign
+v +1.0 +0.5 +100
+
+# v7: leading zeros
+v 007.5 -003.14 000.001
+
+# v8: very small subnormal-range value
+v 1e-300 -1e-300 5e-310
+
+# v9: very large value near overflow
+v 1.7976931348623157e+308 -1e+308 1e+307
+
+# v10: negative zero
+v -0 -0.0 -0.0e0
+
+# v11: exponent with leading zeros
+v 1.5e002 -3.0e+007 7e-003
+
+# v12: single digit values
+v 0 1 9
+
+# v13: mixed sign exponents
+v 1e+0 1e-0 -1e+0
+
+# v14: max precision decimal (many digits)
+v 3.141592653589793 2.718281828459045 1.4142135623730951
+
+# v15: one as exponent boundary
+v 1e1 1e-1 -1e1
+
+# Normals to test normal parsing path too
+vn 0.0 1.0 0.0
+vn -0.707107 0.0 0.707107
+vn 1e-5 -1e-5 0.99999
+
+# Texture coords with edge values
+vt 0.0 0.0
+vt 1.0 1.0
+vt 0.5 .5
+vt +0.25 +0.75
+
+f 1//1 2//1 3//1
+f 4//2 5//2 6//2
diff --git a/models/texcoord-w-mixed.obj b/models/texcoord-w-mixed.obj
new file mode 100644
index 00000000..16421ae5
--- /dev/null
+++ b/models/texcoord-w-mixed.obj
@@ -0,0 +1,15 @@
+# OBJ file with mixed 3-component and 2-component texture coordinates
+# Tests that texcoord_ws correctly stores 0.0 for omitted w values
+v 0 0 0
+v 1 0 0
+v 1 1 0
+v 0 1 0
+
+# vt lines alternating: w present, w omitted, w present, w omitted
+vt 0.0 0.0 0.5
+vt 1.0 0.0
+vt 1.0 1.0 0.75
+vt 0.0 1.0
+
+f 1/1 2/2 3/3
+f 1/1 3/3 4/4
diff --git a/models/texcoord-w.obj b/models/texcoord-w.obj
new file mode 100644
index 00000000..019ea7ae
--- /dev/null
+++ b/models/texcoord-w.obj
@@ -0,0 +1,14 @@
+# OBJ file with 3-component texture coordinates to test texcoord_ws parsing
+v 0 0 0
+v 1 0 0
+v 1 1 0
+v 0 1 0
+
+# texture coords with optional w component
+vt 0.0 0.0 0.5
+vt 1.0 0.0 0.25
+vt 1.0 1.0 0.75
+vt 0.0 1.0 0.0
+
+f 1/1 2/2 3/3
+f 1/1 3/3 4/4
diff --git a/models/utf8-path-test.mtl b/models/utf8-path-test.mtl
new file mode 100644
index 00000000..b89434a9
--- /dev/null
+++ b/models/utf8-path-test.mtl
@@ -0,0 +1,4 @@
+newmtl Material
+Ka 0 0 0
+Kd 0.8 0.8 0.8
+Ks 0 0 0
diff --git a/models/utf8-path-test.obj b/models/utf8-path-test.obj
new file mode 100644
index 00000000..a70ea094
--- /dev/null
+++ b/models/utf8-path-test.obj
@@ -0,0 +1,6 @@
+mtllib utf8-path-test.mtl
+v 0.0 0.0 0.0
+v 1.0 0.0 0.0
+v 0.0 1.0 0.0
+usemtl Material
+f 1 2 3
diff --git a/pyproject.toml b/pyproject.toml
index d3ba7cf3..bcf10b1c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -14,9 +14,21 @@ build-backend = "setuptools.build_meta"
[tool.black]
line-length = 140
+force-exclude = '''
+(
+ /deps/.*$
+ | /kuroga.py$
+ | /config-msvc.py$
+ | /config-posix.py$
+ | ^python/build/.*$
+ | ^python/dist/.*$
+ | ^python/tinyobjloader.egg-info/.*$
+)
+'''
[project]
name = "tinyobjloader"
+license = { text = "MIT AND ISC" }
# version: Use setuptools_scm
dynamic = ["version", "classifiers", "authors", "description"]
@@ -37,3 +49,7 @@ readme = {file = "README.md", content-type = "text/markdown"}
# setuptools_scm<8
write_to = "python/_version.py"
+
+[tool.cibuildwheel]
+# Disable aarch64 build since it's too slow to build(docker + qemu).
+skip = ["cp38-*", "cp314t-*", "*_aarch64*"]
diff --git a/python/bindings.cc b/python/bindings.cc
index e7e6c951..a303e01f 100644
--- a/python/bindings.cc
+++ b/python/bindings.cc
@@ -149,9 +149,10 @@ PYBIND11_MODULE(tinyobjloader, tobj_module)
.def(py::init<>())
.def_readonly("num_face_vertices", &mesh_t::num_face_vertices)
.def("numpy_num_face_vertices", [] (mesh_t &instance) {
- auto ret = py::array_t(instance.num_face_vertices.size());
+ using T = typename std::remove_reference::type::value_type;
+ auto ret = py::array_t(instance.num_face_vertices.size());
py::buffer_info buf = ret.request();
- memcpy(buf.ptr, instance.num_face_vertices.data(), instance.num_face_vertices.size() * sizeof(unsigned char));
+ memcpy(buf.ptr, instance.num_face_vertices.data(), instance.num_face_vertices.size() * sizeof(T));
return ret;
})
.def("vertex_indices", [](mesh_t &self) {
diff --git a/sandbox/parse_fp/CMakeLists.txt b/sandbox/parse_fp/CMakeLists.txt
new file mode 100644
index 00000000..c0c9df9e
--- /dev/null
+++ b/sandbox/parse_fp/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.0)
+project(parse_fp_test CXX)
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+add_executable(test_parse_fp test_parse_fp.cc)
+
+# Optional: enable optimizations for benchmark mode
+if(CMAKE_BUILD_TYPE STREQUAL "Release")
+ if(MSVC)
+ target_compile_options(test_parse_fp PRIVATE /O2)
+ else()
+ target_compile_options(test_parse_fp PRIVATE -O2)
+ endif()
+endif()
diff --git a/sandbox/parse_fp/LICENSE-APACHE b/sandbox/parse_fp/LICENSE-APACHE
new file mode 100644
index 00000000..26f4398f
--- /dev/null
+++ b/sandbox/parse_fp/LICENSE-APACHE
@@ -0,0 +1,190 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ 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
+ limitations under the License.
diff --git a/sandbox/parse_fp/LICENSE-BOOST b/sandbox/parse_fp/LICENSE-BOOST
new file mode 100644
index 00000000..127a5bc3
--- /dev/null
+++ b/sandbox/parse_fp/LICENSE-BOOST
@@ -0,0 +1,23 @@
+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.
\ No newline at end of file
diff --git a/sandbox/parse_fp/LICENSE-MIT b/sandbox/parse_fp/LICENSE-MIT
new file mode 100644
index 00000000..2fb2a37a
--- /dev/null
+++ b/sandbox/parse_fp/LICENSE-MIT
@@ -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/sandbox/parse_fp/fast_float.h b/sandbox/parse_fp/fast_float.h
new file mode 100644
index 00000000..cb044c28
--- /dev/null
+++ b/sandbox/parse_fp/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()
+#include
+#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
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef __has_include
+#if __has_include() && (__cplusplus > 202002L || (defined(_MSVC_LANG) && (_MSVC_LANG > 202002L)))
+#include
+#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 struct from_chars_result_t {
+ UC const *ptr;
+ std::errc ec;
+};
+
+using from_chars_result = from_chars_result_t;
+
+template 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;
+
+} // namespace fast_float
+
+#if FASTFLOAT_HAS_BIT_CAST
+#include
+#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
+#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
+#elif defined(sun) || defined(__sun)
+#include
+#elif defined(__MVS__)
+#include
+#else
+#ifdef __has_include
+#if __has_include()
+#include
+#endif //__has_include()
+#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
+struct is_supported_float_type
+ : std::integral_constant<
+ bool, std::is_same::value || std::is_same::value
+#ifdef __STDCPP_FLOAT64_T__
+ || std::is_same::value
+#endif
+#ifdef __STDCPP_FLOAT32_T__
+ || std::is_same::value
+#endif
+#ifdef __STDCPP_FLOAT16_T__
+ || std::is_same::value
+#endif
+#ifdef __STDCPP_BFLOAT16_T__
+ || std::is_same::value
+#endif
+ > {
+};
+
+template
+using equiv_uint_t = typename std::conditional<
+ sizeof(T) == 1, uint8_t,
+ typename std::conditional<
+ sizeof(T) == 2, uint16_t,
+ typename std::conditional::type>::type>::type;
+
+template struct is_supported_integer_type : std::is_integral {};
+
+template
+struct is_supported_char_type
+ : std::integral_constant::value ||
+ std::is_same::value ||
+ std::is_same::value ||
+ std::is_same::value
+#ifdef __cpp_char8_t
+ || std::is_same::value
+#endif
+ > {
+};
+
+// Compares two ASCII strings in a case insensitive manner.
+template
+inline FASTFLOAT_CONSTEXPR14 bool
+fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
+ size_t length) {
+ for (size_t i = 0; i < length; ++i) {
+ UC const actual = actual_mixedcase[i];
+ if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+#ifndef FLT_EVAL_METHOD
+#error "FLT_EVAL_METHOD should be defined, please include cfloat."
+#endif
+
+// a pointer and a length to a contiguous block of memory
+template struct span {
+ T const *ptr;
+ size_t length;
+
+ constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {}
+
+ constexpr span() : ptr(nullptr), length(0) {}
+
+ constexpr size_t len() const noexcept { return length; }
+
+ FASTFLOAT_CONSTEXPR14 const T &operator[](size_t index) const noexcept {
+ FASTFLOAT_DEBUG_ASSERT(index < length);
+ return ptr[index];
+ }
+};
+
+struct value128 {
+ uint64_t low;
+ uint64_t high;
+
+ constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
+
+ constexpr value128() : low(0), high(0) {}
+};
+
+/* Helper C++14 constexpr generic implementation of leading_zeroes */
+fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int
+leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
+ if (input_num & uint64_t(0xffffffff00000000)) {
+ input_num >>= 32;
+ last_bit |= 32;
+ }
+ if (input_num & uint64_t(0xffff0000)) {
+ input_num >>= 16;
+ last_bit |= 16;
+ }
+ if (input_num & uint64_t(0xff00)) {
+ input_num >>= 8;
+ last_bit |= 8;
+ }
+ if (input_num & uint64_t(0xf0)) {
+ input_num >>= 4;
+ last_bit |= 4;
+ }
+ if (input_num & uint64_t(0xc)) {
+ input_num >>= 2;
+ last_bit |= 2;
+ }
+ if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */
+ last_bit |= 1;
+ }
+ return 63 - last_bit;
+}
+
+/* result might be undefined when input_num is zero */
+fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int
+leading_zeroes(uint64_t input_num) {
+ assert(input_num > 0);
+ if (cpp20_and_in_constexpr()) {
+ return leading_zeroes_generic(input_num);
+ }
+#ifdef FASTFLOAT_VISUAL_STUDIO
+#if defined(_M_X64) || defined(_M_ARM64)
+ unsigned long leading_zero = 0;
+ // Search the mask data from most significant bit (MSB)
+ // to least significant bit (LSB) for a set bit (1).
+ _BitScanReverse64(&leading_zero, input_num);
+ return (int)(63 - leading_zero);
+#else
+ return leading_zeroes_generic(input_num);
+#endif
+#else
+ return __builtin_clzll(input_num);
+#endif
+}
+
+// slow emulation routine for 32-bit
+fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) {
+ return x * (uint64_t)y;
+}
+
+fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
+umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
+ uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
+ uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
+ uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
+ uint64_t adbc_carry = (uint64_t)(adbc < ad);
+ uint64_t lo = bd + (adbc << 32);
+ *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
+ (adbc_carry << 32) + (uint64_t)(lo < bd);
+ return lo;
+}
+
+#ifdef FASTFLOAT_32BIT
+
+// slow emulation routine for 32-bit
+#if !defined(__MINGW64__)
+fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab,
+ uint64_t cd,
+ uint64_t *hi) {
+ return umul128_generic(ab, cd, hi);
+}
+#endif // !__MINGW64__
+
+#endif // FASTFLOAT_32BIT
+
+// compute 64-bit a*b
+fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
+full_multiplication(uint64_t a, uint64_t b) {
+ if (cpp20_and_in_constexpr()) {
+ value128 answer;
+ answer.low = umul128_generic(a, b, &answer.high);
+ return answer;
+ }
+ value128 answer;
+#if defined(_M_ARM64) && !defined(__MINGW32__)
+ // ARM64 has native support for 64-bit multiplications, no need to emulate
+ // But MinGW on ARM64 doesn't have native support for 64-bit multiplications
+ answer.high = __umulh(a, b);
+ answer.low = a * b;
+#elif defined(FASTFLOAT_32BIT) || \
+ (defined(_WIN64) && !defined(__clang__) && !defined(_M_ARM64))
+ answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
+#elif defined(FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__)
+ __uint128_t r = ((__uint128_t)a) * b;
+ answer.low = uint64_t(r);
+ answer.high = uint64_t(r >> 64);
+#else
+ answer.low = umul128_generic(a, b, &answer.high);
+#endif
+ return answer;
+}
+
+struct adjusted_mantissa {
+ uint64_t mantissa{0};
+ int32_t power2{0}; // a negative value indicates an invalid result
+ adjusted_mantissa() = default;
+
+ constexpr bool operator==(adjusted_mantissa const &o) const {
+ return mantissa == o.mantissa && power2 == o.power2;
+ }
+
+ constexpr bool operator!=(adjusted_mantissa const &o) const {
+ return mantissa != o.mantissa || power2 != o.power2;
+ }
+};
+
+// Bias so we can get the real exponent with an invalid adjusted_mantissa.
+constexpr static int32_t invalid_am_bias = -0x8000;
+
+// used for binary_format_lookup_tables::max_mantissa
+constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
+
+template struct binary_format_lookup_tables;
+
+template struct binary_format : binary_format_lookup_tables {
+ using equiv_uint = equiv_uint_t;
+
+ static constexpr int mantissa_explicit_bits();
+ static constexpr int minimum_exponent();
+ static constexpr int infinite_power();
+ static constexpr int sign_index();
+ static constexpr int
+ min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
+ static constexpr int max_exponent_fast_path();
+ static constexpr int max_exponent_round_to_even();
+ static constexpr int min_exponent_round_to_even();
+ static constexpr uint64_t max_mantissa_fast_path(int64_t power);
+ static constexpr uint64_t
+ max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
+ static constexpr int largest_power_of_ten();
+ static constexpr int smallest_power_of_ten();
+ static constexpr T exact_power_of_ten(int64_t power);
+ static constexpr size_t max_digits();
+ static constexpr equiv_uint exponent_mask();
+ static constexpr equiv_uint mantissa_mask();
+ static constexpr equiv_uint hidden_bit_mask();
+};
+
+template struct binary_format_lookup_tables {
+ static constexpr double powers_of_ten[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
+ 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
+
+ // Largest integer value v so that (5**index * v) <= 1<<53.
+ // 0x20000000000000 == 1 << 53
+ static constexpr uint64_t max_mantissa[] = {
+ 0x20000000000000,
+ 0x20000000000000 / 5,
+ 0x20000000000000 / (5 * 5),
+ 0x20000000000000 / (5 * 5 * 5),
+ 0x20000000000000 / (5 * 5 * 5 * 5),
+ 0x20000000000000 / (constant_55555),
+ 0x20000000000000 / (constant_55555 * 5),
+ 0x20000000000000 / (constant_55555 * 5 * 5),
+ 0x20000000000000 / (constant_55555 * 5 * 5 * 5),
+ 0x20000000000000 / (constant_55555 * 5 * 5 * 5 * 5),
+ 0x20000000000000 / (constant_55555 * constant_55555),
+ 0x20000000000000 / (constant_55555 * constant_55555 * 5),
+ 0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5),
+ 0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5),
+ 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555),
+ 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5),
+ 0x20000000000000 /
+ (constant_55555 * constant_55555 * constant_55555 * 5 * 5),
+ 0x20000000000000 /
+ (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
+ 0x20000000000000 /
+ (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5),
+ 0x20000000000000 /
+ (constant_55555 * constant_55555 * constant_55555 * constant_55555),
+ 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
+ constant_55555 * 5),
+ 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
+ constant_55555 * 5 * 5),
+ 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
+ constant_55555 * 5 * 5 * 5),
+ 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
+ constant_55555 * 5 * 5 * 5 * 5)};
+};
+
+#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
+
+template
+constexpr double binary_format_lookup_tables::powers_of_ten[];
+
+template
+constexpr uint64_t binary_format_lookup_tables::max_mantissa[];
+
+#endif
+
+template struct binary_format_lookup_tables {
+ static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
+ 1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
+
+ // Largest integer value v so that (5**index * v) <= 1<<24.
+ // 0x1000000 == 1<<24
+ static constexpr uint64_t max_mantissa[] = {
+ 0x1000000,
+ 0x1000000 / 5,
+ 0x1000000 / (5 * 5),
+ 0x1000000 / (5 * 5 * 5),
+ 0x1000000 / (5 * 5 * 5 * 5),
+ 0x1000000 / (constant_55555),
+ 0x1000000 / (constant_55555 * 5),
+ 0x1000000 / (constant_55555 * 5 * 5),
+ 0x1000000 / (constant_55555 * 5 * 5 * 5),
+ 0x1000000 / (constant_55555 * 5 * 5 * 5 * 5),
+ 0x1000000 / (constant_55555 * constant_55555),
+ 0x1000000 / (constant_55555 * constant_55555 * 5)};
+};
+
+#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
+
+template
+constexpr float binary_format_lookup_tables::powers_of_ten[];
+
+template
+constexpr uint64_t binary_format_lookup_tables::max_mantissa[];
+
+#endif
+
+template <>
+inline constexpr int binary_format::min_exponent_fast_path() {
+#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
+ return 0;
+#else
+ return -22;
+#endif
+}
+
+template <>
+inline constexpr int binary_format::min_exponent_fast_path() {
+#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
+ return 0;
+#else
+ return -10;
+#endif
+}
+
+template <>
+inline constexpr int binary_format::mantissa_explicit_bits() {
+ return 52;
+}
+
+template <>
+inline constexpr int binary_format::mantissa_explicit_bits() {
+ return 23;
+}
+
+template <>
+inline constexpr int binary_format::max_exponent_round_to_even() {
+ return 23;
+}
+
+template <>
+inline constexpr int binary_format::max_exponent_round_to_even() {
+ return 10;
+}
+
+template <>
+inline constexpr int binary_format::min_exponent_round_to_even() {
+ return -4;
+}
+
+template <>
+inline constexpr int binary_format::min_exponent_round_to_even() {
+ return -17;
+}
+
+template <> inline constexpr int binary_format::minimum_exponent() {
+ return -1023;
+}
+
+template <> inline constexpr int binary_format::minimum_exponent() {
+ return -127;
+}
+
+template <> inline constexpr int binary_format::infinite_power() {
+ return 0x7FF;
+}
+
+template <> inline constexpr int binary_format::infinite_power() {
+ return 0xFF;
+}
+
+template <> inline constexpr int binary_format::sign_index() {
+ return 63;
+}
+
+template <> inline constexpr int binary_format::sign_index() {
+ return 31;
+}
+
+template <>
+inline constexpr int binary_format::max_exponent_fast_path() {
+ return 22;
+}
+
+template <>
+inline constexpr int binary_format::max_exponent_fast_path() {
+ return 10;
+}
+
+template <>
+inline constexpr uint64_t binary_format::max_mantissa_fast_path() {
+ return uint64_t(2) << mantissa_explicit_bits();
+}
+
+template <>
+inline constexpr uint64_t binary_format::max_mantissa_fast_path() {
+ return uint64_t(2) << mantissa_explicit_bits();
+}
+
+// credit: Jakub Jelínek
+#ifdef __STDCPP_FLOAT16_T__
+template struct binary_format_lookup_tables {
+ static constexpr std::float16_t powers_of_ten[] = {1e0f16, 1e1f16, 1e2f16,
+ 1e3f16, 1e4f16};
+
+ // Largest integer value v so that (5**index * v) <= 1<<11.
+ // 0x800 == 1<<11
+ static constexpr uint64_t max_mantissa[] = {0x800,
+ 0x800 / 5,
+ 0x800 / (5 * 5),
+ 0x800 / (5 * 5 * 5),
+ 0x800 / (5 * 5 * 5 * 5),
+ 0x800 / (constant_55555)};
+};
+
+#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
+
+template
+constexpr std::float16_t
+ binary_format_lookup_tables::powers_of_ten[];
+
+template
+constexpr uint64_t
+ binary_format_lookup_tables::max_mantissa[];
+
+#endif
+
+template <>
+inline constexpr std::float16_t
+binary_format::exact_power_of_ten(int64_t power) {
+ // Work around clang bug https://godbolt.org/z/zedh7rrhc
+ return (void)powers_of_ten[0], powers_of_ten[power];
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::exponent_mask() {
+ return 0x7C00;
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::mantissa_mask() {
+ return 0x03FF;
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::hidden_bit_mask() {
+ return 0x0400;
+}
+
+template <>
+inline constexpr int binary_format::max_exponent_fast_path() {
+ return 4;
+}
+
+template <>
+inline constexpr int binary_format::mantissa_explicit_bits() {
+ return 10;
+}
+
+template <>
+inline constexpr uint64_t
+binary_format::max_mantissa_fast_path() {
+ return uint64_t(2) << mantissa_explicit_bits();
+}
+
+template <>
+inline constexpr uint64_t
+binary_format::max_mantissa_fast_path(int64_t power) {
+ // caller is responsible to ensure that
+ // power >= 0 && power <= 4
+ //
+ // Work around clang bug https://godbolt.org/z/zedh7rrhc
+ return (void)max_mantissa[0], max_mantissa[power];
+}
+
+template <>
+inline constexpr int binary_format::min_exponent_fast_path() {
+ return 0;
+}
+
+template <>
+inline constexpr int
+binary_format::max_exponent_round_to_even() {
+ return 5;
+}
+
+template <>
+inline constexpr int
+binary_format::min_exponent_round_to_even() {
+ return -22;
+}
+
+template <>
+inline constexpr int binary_format::minimum_exponent() {
+ return -15;
+}
+
+template <>
+inline constexpr int binary_format::infinite_power() {
+ return 0x1F;
+}
+
+template <> inline constexpr int binary_format::sign_index() {
+ return 15;
+}
+
+template <>
+inline constexpr int binary_format::largest_power_of_ten() {
+ return 4;
+}
+
+template <>
+inline constexpr int binary_format::smallest_power_of_ten() {
+ return -27;
+}
+
+template <>
+inline constexpr size_t binary_format::max_digits() {
+ return 22;
+}
+#endif // __STDCPP_FLOAT16_T__
+
+// credit: Jakub Jelínek
+#ifdef __STDCPP_BFLOAT16_T__
+template struct binary_format_lookup_tables {
+ static constexpr std::bfloat16_t powers_of_ten[] = {1e0bf16, 1e1bf16, 1e2bf16,
+ 1e3bf16};
+
+ // Largest integer value v so that (5**index * v) <= 1<<8.
+ // 0x100 == 1<<8
+ static constexpr uint64_t max_mantissa[] = {0x100, 0x100 / 5, 0x100 / (5 * 5),
+ 0x100 / (5 * 5 * 5),
+ 0x100 / (5 * 5 * 5 * 5)};
+};
+
+#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
+
+template
+constexpr std::bfloat16_t
+ binary_format_lookup_tables::powers_of_ten[];
+
+template
+constexpr uint64_t
+ binary_format_lookup_tables::max_mantissa[];
+
+#endif
+
+template <>
+inline constexpr std::bfloat16_t
+binary_format::exact_power_of_ten(int64_t power) {
+ // Work around clang bug https://godbolt.org/z/zedh7rrhc
+ return (void)powers_of_ten[0], powers_of_ten[power];
+}
+
+template <>
+inline constexpr int binary_format::max_exponent_fast_path() {
+ return 3;
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::exponent_mask() {
+ return 0x7F80;
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::mantissa_mask() {
+ return 0x007F;
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::hidden_bit_mask() {
+ return 0x0080;
+}
+
+template <>
+inline constexpr int binary_format::mantissa_explicit_bits() {
+ return 7;
+}
+
+template <>
+inline constexpr uint64_t
+binary_format::max_mantissa_fast_path() {
+ return uint64_t(2) << mantissa_explicit_bits();
+}
+
+template <>
+inline constexpr uint64_t
+binary_format::max_mantissa_fast_path(int64_t power) {
+ // caller is responsible to ensure that
+ // power >= 0 && power <= 3
+ //
+ // Work around clang bug https://godbolt.org/z/zedh7rrhc
+ return (void)max_mantissa[0], max_mantissa[power];
+}
+
+template <>
+inline constexpr int binary_format::min_exponent_fast_path() {
+ return 0;
+}
+
+template <>
+inline constexpr int
+binary_format::max_exponent_round_to_even() {
+ return 3;
+}
+
+template <>
+inline constexpr int
+binary_format::min_exponent_round_to_even() {
+ return -24;
+}
+
+template <>
+inline constexpr int binary_format::minimum_exponent() {
+ return -127;
+}
+
+template <>
+inline constexpr int binary_format::infinite_power() {
+ return 0xFF;
+}
+
+template <> inline constexpr int binary_format::sign_index() {
+ return 15;
+}
+
+template <>
+inline constexpr int binary_format::largest_power_of_ten() {
+ return 38;
+}
+
+template <>
+inline constexpr int binary_format::smallest_power_of_ten() {
+ return -60;
+}
+
+template <>
+inline constexpr size_t binary_format::max_digits() {
+ return 98;
+}
+#endif // __STDCPP_BFLOAT16_T__
+
+template <>
+inline constexpr uint64_t
+binary_format::max_mantissa_fast_path(int64_t power) {
+ // caller is responsible to ensure that
+ // power >= 0 && power <= 22
+ //
+ // Work around clang bug https://godbolt.org/z/zedh7rrhc
+ return (void)max_mantissa[0], max_mantissa[power];
+}
+
+template <>
+inline constexpr uint64_t
+binary_format::max_mantissa_fast_path(int64_t power) {
+ // caller is responsible to ensure that
+ // power >= 0 && power <= 10
+ //
+ // Work around clang bug https://godbolt.org/z/zedh7rrhc
+ return (void)max_mantissa[0], max_mantissa[power];
+}
+
+template <>
+inline constexpr double
+binary_format::exact_power_of_ten(int64_t power) {
+ // Work around clang bug https://godbolt.org/z/zedh7rrhc
+ return (void)powers_of_ten[0], powers_of_ten[power];
+}
+
+template <>
+inline constexpr float binary_format::exact_power_of_ten(int64_t power) {
+ // Work around clang bug https://godbolt.org/z/zedh7rrhc
+ return (void)powers_of_ten[0], powers_of_ten[power];
+}
+
+template <> inline constexpr int binary_format::largest_power_of_ten() {
+ return 308;
+}
+
+template <> inline constexpr int binary_format::largest_power_of_ten() {
+ return 38;
+}
+
+template <>
+inline constexpr int binary_format::smallest_power_of_ten() {
+ return -342;
+}
+
+template <> inline constexpr int binary_format::smallest_power_of_ten() {
+ return -64;
+}
+
+template <> inline constexpr size_t binary_format::max_digits() {
+ return 769;
+}
+
+template <> inline constexpr size_t binary_format::max_digits() {
+ return 114;
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::exponent_mask() {
+ return 0x7F800000;
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::exponent_mask() {
+ return 0x7FF0000000000000;
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::mantissa_mask() {
+ return 0x007FFFFF;
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::mantissa_mask() {
+ return 0x000FFFFFFFFFFFFF;
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::hidden_bit_mask() {
+ return 0x00800000;
+}
+
+template <>
+inline constexpr binary_format::equiv_uint
+binary_format::hidden_bit_mask() {
+ return 0x0010000000000000;
+}
+
+template
+fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
+to_float(bool negative, adjusted_mantissa am, T &value) {
+ using equiv_uint = equiv_uint_t;
+ equiv_uint word = equiv_uint(am.mantissa);
+ word = equiv_uint(word | equiv_uint(am.power2)
+ << binary_format::mantissa_explicit_bits());
+ word =
+ equiv_uint(word | equiv_uint(negative) << binary_format::sign_index());
+#if FASTFLOAT_HAS_BIT_CAST
+ value = std::bit_cast(word);
+#else
+ ::memcpy(&value, &word, sizeof(T));
+#endif
+}
+
+template struct space_lut {
+ static constexpr bool value[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+};
+
+#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
+
+template constexpr bool space_lut::value[];
+
+#endif
+
+template constexpr bool is_space(UC c) {
+ return c < 256 && space_lut<>::value[uint8_t(c)];
+}
+
+template static constexpr uint64_t int_cmp_zeros() {
+ static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
+ "Unsupported character size");
+ return (sizeof(UC) == 1) ? 0x3030303030303030
+ : (sizeof(UC) == 2)
+ ? (uint64_t(UC('0')) << 48 | uint64_t(UC('0')) << 32 |
+ uint64_t(UC('0')) << 16 | UC('0'))
+ : (uint64_t(UC('0')) << 32 | UC('0'));
+}
+
+template static constexpr int int_cmp_len() {
+ return sizeof(uint64_t) / sizeof(UC);
+}
+
+template constexpr UC const *str_const_nan();
+
+template <> constexpr char const *str_const_nan() { return "nan"; }
+
+template <> constexpr wchar_t const *str_const_nan() { return L"nan"; }
+
+template <> constexpr char16_t const *str_const_nan() {
+ return u"nan";
+}
+
+template <> constexpr char32_t const *str_const_nan() {
+ return U"nan";
+}
+
+#ifdef __cpp_char8_t
+template <> constexpr char8_t const *str_const_nan() {
+ return u8"nan";
+}
+#endif
+
+template constexpr UC const *str_const_inf();
+
+template <> constexpr char const *str_const_inf() { return "infinity"; }
+
+template <> constexpr wchar_t const *str_const_inf() {
+ return L"infinity";
+}
+
+template <> constexpr char16_t const *str_const_inf() {
+ return u"infinity";
+}
+
+template <> constexpr char32_t const *str_const_inf() {
+ return U"infinity";
+}
+
+#ifdef __cpp_char8_t
+template <> constexpr char8_t const *str_const_inf() {
+ return u8"infinity";
+}
+#endif
+
+template struct int_luts {
+ static constexpr uint8_t chdigit[] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255,
+ 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255};
+
+ static constexpr size_t maxdigits_u64[] = {
+ 64, 41, 32, 28, 25, 23, 22, 21, 20, 19, 18, 18, 17, 17, 16, 16, 16, 16,
+ 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13};
+
+ static constexpr uint64_t min_safe_u64[] = {
+ 9223372036854775808ull, 12157665459056928801ull, 4611686018427387904,
+ 7450580596923828125, 4738381338321616896, 3909821048582988049,
+ 9223372036854775808ull, 12157665459056928801ull, 10000000000000000000ull,
+ 5559917313492231481, 2218611106740436992, 8650415919381337933,
+ 2177953337809371136, 6568408355712890625, 1152921504606846976,
+ 2862423051509815793, 6746640616477458432, 15181127029874798299ull,
+ 1638400000000000000, 3243919932521508681, 6221821273427820544,
+ 11592836324538749809ull, 876488338465357824, 1490116119384765625,
+ 2481152873203736576, 4052555153018976267, 6502111422497947648,
+ 10260628712958602189ull, 15943230000000000000ull, 787662783788549761,
+ 1152921504606846976, 1667889514952984961, 2386420683693101056,
+ 3379220508056640625, 4738381338321616896};
+};
+
+#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
+
+template constexpr uint8_t int_luts::chdigit[];
+
+template constexpr size_t int_luts::maxdigits_u64[];
+
+template constexpr uint64_t int_luts::min_safe_u64[];
+
+#endif
+
+template
+fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
+ return int_luts<>::chdigit[static_cast(c)];
+}
+
+fastfloat_really_inline constexpr size_t max_digits_u64(int base) {
+ return int_luts<>::maxdigits_u64[base - 2];
+}
+
+// If a u64 is exactly max_digits_u64() in length, this is
+// the value below which it has definitely overflowed.
+fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) {
+ return int_luts<>::min_safe_u64[base - 2];
+}
+
+static_assert(std::is_same, uint64_t>::value,
+ "equiv_uint should be uint64_t for double");
+static_assert(std::numeric_limits::is_iec559,
+ "double must fulfill the requirements of IEC 559 (IEEE 754)");
+
+static_assert(std::is_same, uint32_t>::value,
+ "equiv_uint should be uint32_t for float");
+static_assert(std::numeric_limits::is_iec559,
+ "float must fulfill the requirements of IEC 559 (IEEE 754)");
+
+#ifdef __STDCPP_FLOAT64_T__
+static_assert(std::is_same, uint64_t>::value,
+ "equiv_uint should be uint64_t for std::float64_t");
+static_assert(
+ std::numeric_limits::is_iec559,
+ "std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)");
+#endif // __STDCPP_FLOAT64_T__
+
+#ifdef __STDCPP_FLOAT32_T__
+static_assert(std::is_same, uint32_t>::value,
+ "equiv_uint should be uint32_t for std::float32_t");
+static_assert(
+ std::numeric_limits::is_iec559,
+ "std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)");
+#endif // __STDCPP_FLOAT32_T__
+
+#ifdef __STDCPP_FLOAT16_T__
+static_assert(
+ std::is_same::equiv_uint, uint16_t>::value,
+ "equiv_uint should be uint16_t for std::float16_t");
+static_assert(
+ std::numeric_limits::is_iec559,
+ "std::float16_t must fulfill the requirements of IEC 559 (IEEE 754)");
+#endif // __STDCPP_FLOAT16_T__
+
+#ifdef __STDCPP_BFLOAT16_T__
+static_assert(
+ std::is_same::equiv_uint, uint16_t>::value,
+ "equiv_uint should be uint16_t for std::bfloat16_t");
+static_assert(
+ std::numeric_limits::is_iec559,
+ "std::bfloat16_t must fulfill the requirements of IEC 559 (IEEE 754)");
+#endif // __STDCPP_BFLOAT16_T__
+
+constexpr chars_format operator~(chars_format rhs) noexcept {
+ using int_type = std::underlying_type::type;
+ return static_cast(~static_cast(rhs));
+}
+
+constexpr chars_format operator&(chars_format lhs, chars_format rhs) noexcept {
+ using int_type = std::underlying_type::type;
+ return static_cast(static_cast(lhs) &
+ static_cast(rhs));
+}
+
+constexpr chars_format operator|(chars_format lhs, chars_format rhs) noexcept {
+ using int_type = std::underlying_type::type;
+ return static_cast(static_cast(lhs) |
+ static_cast(rhs));
+}
+
+constexpr chars_format operator^(chars_format lhs, chars_format rhs) noexcept {
+ using int_type = std::underlying_type::type;
+ return static_cast(static_cast(lhs) ^
+ static_cast(rhs));
+}
+
+fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
+operator&=(chars_format &lhs, chars_format rhs) noexcept {
+ return lhs = (lhs & rhs);
+}
+
+fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
+operator|=(chars_format &lhs, chars_format rhs) noexcept {
+ return lhs = (lhs | rhs);
+}
+
+fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
+operator^=(chars_format &lhs, chars_format rhs) noexcept {
+ return lhs = (lhs ^ rhs);
+}
+
+namespace detail {
+// adjust for deprecated feature macros
+constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
+ return fmt
+#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS
+ | chars_format::allow_leading_plus
+#endif
+#ifdef FASTFLOAT_SKIP_WHITE_SPACE
+ | chars_format::skip_white_space
+#endif
+ ;
+}
+} // namespace detail
+
+} // namespace fast_float
+
+#endif
+
+
+#ifndef FASTFLOAT_FAST_FLOAT_H
+#define FASTFLOAT_FAST_FLOAT_H
+
+
+namespace fast_float {
+/**
+ * This function parses the character sequence [first,last) for a number. It
+ * parses floating-point numbers expecting a locale-indepent format equivalent
+ * to what is used by std::strtod in the default ("C") locale. The resulting
+ * floating-point value is the closest floating-point values (using either float
+ * or double), using the "round to even" convention for values that would
+ * otherwise fall right in-between two values. That is, we provide exact parsing
+ * according to the IEEE standard.
+ *
+ * Given a successful parse, the pointer (`ptr`) in the returned value is set to
+ * point right after the parsed number, and the `value` referenced is set to the
+ * parsed value. In case of error, the returned `ec` contains a representative
+ * error, otherwise the default (`std::errc()`) value is stored.
+ *
+ * The implementation does not throw and does not allocate memory (e.g., with
+ * `new` or `malloc`).
+ *
+ * Like the C++17 standard, the `fast_float::from_chars` functions take an
+ * optional last argument of the type `fast_float::chars_format`. It is a bitset
+ * value: we check whether `fmt & fast_float::chars_format::fixed` and `fmt &
+ * fast_float::chars_format::scientific` are set to determine whether we allow
+ * the fixed point and scientific notation respectively. The default is
+ * `fast_float::chars_format::general` which allows both `fixed` and
+ * `scientific`.
+ */
+template ::value)>
+FASTFLOAT_CONSTEXPR20 from_chars_result_t
+from_chars(UC const *first, UC const *last, T &value,
+ chars_format fmt = chars_format::general) noexcept;
+
+/**
+ * Like from_chars, but accepts an `options` argument to govern number parsing.
+ * Both for floating-point types and integer types.
+ */
+template
+FASTFLOAT_CONSTEXPR20 from_chars_result_t
+from_chars_advanced(UC const *first, UC const *last, T &value,
+ parse_options_t options) noexcept;
+
+/**
+ * from_chars for integer types.
+ */
+template ::value)>
+FASTFLOAT_CONSTEXPR20 from_chars_result_t
+from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept;
+
+} // namespace fast_float
+
+#endif // FASTFLOAT_FAST_FLOAT_H
+
+#ifndef FASTFLOAT_ASCII_NUMBER_H
+#define FASTFLOAT_ASCII_NUMBER_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#ifdef FASTFLOAT_SSE2
+#include
+#endif
+
+#ifdef FASTFLOAT_NEON
+#include
+#endif
+
+namespace fast_float {
+
+template fastfloat_really_inline constexpr bool has_simd_opt() {
+#ifdef FASTFLOAT_HAS_SIMD
+ return std::is_same::value;
+#else
+ return false;
+#endif
+}
+
+// Next function can be micro-optimized, but compilers are entirely
+// able to optimize it well.
+template
+fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
+ return !(c > UC('9') || c < UC('0'));
+}
+
+fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
+ return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
+ (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
+ (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
+ (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
+}
+
+// Read 8 UC into a u64. Truncates UC if not char.
+template
+fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
+read8_to_u64(UC const *chars) {
+ if (cpp20_and_in_constexpr() || !std::is_same::value) {
+ uint64_t val = 0;
+ for (int i = 0; i < 8; ++i) {
+ val |= uint64_t(uint8_t(*chars)) << (i * 8);
+ ++chars;
+ }
+ return val;
+ }
+ uint64_t val;
+ ::memcpy(&val, chars, sizeof(uint64_t));
+#if FASTFLOAT_IS_BIG_ENDIAN == 1
+ // Need to read as-if the number was in little-endian order.
+ val = byteswap(val);
+#endif
+ return val;
+}
+
+#ifdef FASTFLOAT_SSE2
+
+fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) {
+ FASTFLOAT_SIMD_DISABLE_WARNINGS
+ __m128i const packed = _mm_packus_epi16(data, data);
+#ifdef FASTFLOAT_64BIT
+ return uint64_t(_mm_cvtsi128_si64(packed));
+#else
+ uint64_t value;
+ // Visual Studio + older versions of GCC don't support _mm_storeu_si64
+ _mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed);
+ return value;
+#endif
+ FASTFLOAT_SIMD_RESTORE_WARNINGS
+}
+
+fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
+ FASTFLOAT_SIMD_DISABLE_WARNINGS
+ return simd_read8_to_u64(
+ _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars)));
+ FASTFLOAT_SIMD_RESTORE_WARNINGS
+}
+
+#elif defined(FASTFLOAT_NEON)
+
+fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const data) {
+ FASTFLOAT_SIMD_DISABLE_WARNINGS
+ uint8x8_t utf8_packed = vmovn_u16(data);
+ return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
+ FASTFLOAT_SIMD_RESTORE_WARNINGS
+}
+
+fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
+ FASTFLOAT_SIMD_DISABLE_WARNINGS
+ return simd_read8_to_u64(
+ vld1q_u16(reinterpret_cast(chars)));
+ FASTFLOAT_SIMD_RESTORE_WARNINGS
+}
+
+#endif // FASTFLOAT_SSE2
+
+// MSVC SFINAE is broken pre-VS2017
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+template
+#else
+template ()) = 0>
+#endif
+// dummy for compile
+uint64_t simd_read8_to_u64(UC const *) {
+ return 0;
+}
+
+// credit @aqrit
+fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
+parse_eight_digits_unrolled(uint64_t val) {
+ uint64_t const mask = 0x000000FF000000FF;
+ uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
+ uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
+ val -= 0x3030303030303030;
+ val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
+ val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
+ return uint32_t(val);
+}
+
+// Call this if chars are definitely 8 digits.
+template
+fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t
+parse_eight_digits_unrolled(UC const *chars) noexcept {
+ if (cpp20_and_in_constexpr() || !has_simd_opt()) {
+ return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay
+ }
+ return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
+}
+
+// credit @aqrit
+fastfloat_really_inline constexpr bool
+is_made_of_eight_digits_fast(uint64_t val) noexcept {
+ return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
+ 0x8080808080808080));
+}
+
+#ifdef FASTFLOAT_HAS_SIMD
+
+// Call this if chars might not be 8 digits.
+// Using this style (instead of is_made_of_eight_digits_fast() then
+// parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice.
+fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
+simd_parse_if_eight_digits_unrolled(char16_t const *chars,
+ uint64_t &i) noexcept {
+ if (cpp20_and_in_constexpr()) {
+ return false;
+ }
+#ifdef FASTFLOAT_SSE2
+ FASTFLOAT_SIMD_DISABLE_WARNINGS
+ __m128i const data =
+ _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars));
+
+ // (x - '0') <= 9
+ // http://0x80.pl/articles/simd-parsing-int-sequences.html
+ __m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
+ __m128i const t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
+
+ if (_mm_movemask_epi8(t1) == 0) {
+ i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
+ return true;
+ } else
+ return false;
+ FASTFLOAT_SIMD_RESTORE_WARNINGS
+#elif defined(FASTFLOAT_NEON)
+ FASTFLOAT_SIMD_DISABLE_WARNINGS
+ uint16x8_t const data = vld1q_u16(reinterpret_cast(chars));
+
+ // (x - '0') <= 9
+ // http://0x80.pl/articles/simd-parsing-int-sequences.html
+ uint16x8_t const t0 = vsubq_u16(data, vmovq_n_u16('0'));
+ uint16x8_t const mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
+
+ if (vminvq_u16(mask) == 0xFFFF) {
+ i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
+ return true;
+ } else
+ return false;
+ FASTFLOAT_SIMD_RESTORE_WARNINGS
+#else
+ (void)chars;
+ (void)i;
+ return false;
+#endif // FASTFLOAT_SSE2
+}
+
+#endif // FASTFLOAT_HAS_SIMD
+
+// MSVC SFINAE is broken pre-VS2017
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+template
+#else
+template ()) = 0>
+#endif
+// dummy for compile
+bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) {
+ return 0;
+}
+
+template ::value) = 0>
+fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
+loop_parse_if_eight_digits(UC const *&p, UC const *const pend, uint64_t &i) {
+ if (!has_simd_opt()) {
+ return;
+ }
+ while ((std::distance(p, pend) >= 8) &&
+ simd_parse_if_eight_digits_unrolled(
+ p, i)) { // in rare cases, this will overflow, but that's ok
+ p += 8;
+ }
+}
+
+fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
+loop_parse_if_eight_digits(char const *&p, char const *const pend,
+ uint64_t &i) {
+ // optimizes better than parse_if_eight_digits_unrolled() for UC = char.
+ while ((std::distance(p, pend) >= 8) &&
+ is_made_of_eight_digits_fast(read8_to_u64(p))) {
+ i = i * 100000000 +
+ parse_eight_digits_unrolled(read8_to_u64(
+ p)); // in rare cases, this will overflow, but that's ok
+ p += 8;
+ }
+}
+
+enum class parse_error {
+ no_error,
+ // [JSON-only] The minus sign must be followed by an integer.
+ missing_integer_after_sign,
+ // A sign must be followed by an integer or dot.
+ missing_integer_or_dot_after_sign,
+ // [JSON-only] The integer part must not have leading zeros.
+ leading_zeros_in_integer_part,
+ // [JSON-only] The integer part must have at least one digit.
+ no_digits_in_integer_part,
+ // [JSON-only] If there is a decimal point, there must be digits in the
+ // fractional part.
+ no_digits_in_fractional_part,
+ // The mantissa must have at least one digit.
+ no_digits_in_mantissa,
+ // Scientific notation requires an exponential part.
+ missing_exponential_part,
+};
+
+template struct parsed_number_string_t {
+ int64_t exponent{0};
+ uint64_t mantissa{0};
+ UC const *lastmatch{nullptr};
+ bool negative{false};
+ bool valid{false};
+ bool too_many_digits{false};
+ // contains the range of the significant digits
+ span integer{}; // non-nullable
+ span fraction{}; // nullable
+ parse_error error{parse_error::no_error};
+};
+
+using byte_span = span;
+using parsed_number_string = parsed_number_string_t;
+
+template
+fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t
+report_parse_error(UC const *p, parse_error error) {
+ parsed_number_string_t answer;
+ answer.valid = false;
+ answer.lastmatch = p;
+ answer.error = error;
+ return answer;
+}
+
+// Assuming that you use no more than 19 digits, this will
+// parse an ASCII string.
+template
+fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t
+parse_number_string(UC const *p, UC const *pend,
+ parse_options_t options) noexcept {
+ chars_format const fmt = detail::adjust_for_feature_macros(options.format);
+ UC const decimal_point = options.decimal_point;
+
+ parsed_number_string_t