From eecdbb757b2c43ce3574b4b20cbf8f99ae04f3b8 Mon Sep 17 00:00:00 2001 From: Madhava Jay Date: Tue, 14 Apr 2026 17:32:27 +1000 Subject: [PATCH 1/2] replaced htslib with noodles and created some minor changes --- .gitmodules | 4 + docs/architecture.md | 97 + lint.sh | 13 +- noodles | 1 + rust/Cargo.lock | 937 ++--- rust/Cargo.toml | 10 + rust/bioscript-ffi/src/lib.rs | 154 +- rust/bioscript-formats/Cargo.toml | 5 +- rust/bioscript-formats/src/alignment.rs | 548 +++ rust/bioscript-formats/src/genotype.rs | 456 ++- rust/bioscript-formats/src/lib.rs | 1 + rust/bioscript-formats/src/prepare.rs | 74 +- rust/bioscript-formats/tests/file_formats.rs | 263 ++ .../tests/fixtures/generate_fixtures.sh | 66 + .../tests/fixtures/mini.cram | Bin 0 -> 790 bytes .../tests/fixtures/mini.cram.crai | Bin 0 -> 45 bytes rust/bioscript-formats/tests/fixtures/mini.fa | 51 + .../tests/fixtures/mini.fa.fai | 1 + .../tests/fixtures/mini_bad_ref.fa | 51 + .../tests/fixtures/mini_bad_ref.fa.fai | 1 + rust/vendor/lexical-util/.cargo-ok | 1 + rust/vendor/lexical-util/.cargo_vcs_info.json | 6 + rust/vendor/lexical-util/CODE_OF_CONDUCT.md | 141 + rust/vendor/lexical-util/Cargo.lock | 50 + rust/vendor/lexical-util/Cargo.toml | 132 + rust/vendor/lexical-util/Cargo.toml.orig | 69 + rust/vendor/lexical-util/LICENSE-APACHE | 201 + rust/vendor/lexical-util/LICENSE-MIT | 23 + rust/vendor/lexical-util/README.md | 414 ++ rust/vendor/lexical-util/src/algorithm.rs | 41 + rust/vendor/lexical-util/src/api.rs | 442 +++ rust/vendor/lexical-util/src/ascii.rs | 47 + rust/vendor/lexical-util/src/assert.rs | 57 + rust/vendor/lexical-util/src/bf16.rs | 9 + rust/vendor/lexical-util/src/constants.rs | 122 + rust/vendor/lexical-util/src/digit.rs | 115 + rust/vendor/lexical-util/src/div128.rs | 512 +++ rust/vendor/lexical-util/src/error.rs | 423 +++ .../vendor/lexical-util/src/extended_float.rs | 39 + rust/vendor/lexical-util/src/f16.rs | 9 + .../vendor/lexical-util/src/feature_format.rs | 1453 +++++++ rust/vendor/lexical-util/src/format.rs | 657 ++++ .../vendor/lexical-util/src/format_builder.rs | 3374 +++++++++++++++++ rust/vendor/lexical-util/src/format_flags.rs | 826 ++++ rust/vendor/lexical-util/src/iterator.rs | 414 ++ rust/vendor/lexical-util/src/lib.rs | 144 + rust/vendor/lexical-util/src/libm.rs | 269 ++ rust/vendor/lexical-util/src/mul.rs | 58 + rust/vendor/lexical-util/src/noskip.rs | 280 ++ .../lexical-util/src/not_feature_format.rs | 1433 +++++++ rust/vendor/lexical-util/src/num.rs | 1505 ++++++++ rust/vendor/lexical-util/src/options.rs | 224 ++ .../lexical-util/src/prebuilt_formats.rs | 2173 +++++++++++ rust/vendor/lexical-util/src/result.rs | 8 + rust/vendor/lexical-util/src/skip.rs | 1584 ++++++++ rust/vendor/lexical-util/src/step.rs | 1449 +++++++ .../lexical-util/tests/algorithm_tests.rs | 30 + rust/vendor/lexical-util/tests/ascii_tests.rs | 51 + rust/vendor/lexical-util/tests/bf16_tests.rs | 44 + rust/vendor/lexical-util/tests/digit_tests.rs | 115 + rust/vendor/lexical-util/tests/f16_tests.rs | 41 + .../tests/feature_format_tests.rs | 241 ++ .../tests/format_builder_tests.rs | 60 + .../lexical-util/tests/format_flags_tests.rs | 60 + .../lexical-util/tests/iterator_tests.rs | 115 + .../tests/not_feature_format_tests.rs | 53 + rust/vendor/lexical-util/tests/num_tests.rs | 254 ++ rust/vendor/lexical-util/tests/skip_tests.rs | 590 +++ 68 files changed, 22091 insertions(+), 1000 deletions(-) create mode 100644 docs/architecture.md create mode 160000 noodles create mode 100644 rust/bioscript-formats/src/alignment.rs create mode 100755 rust/bioscript-formats/tests/fixtures/generate_fixtures.sh create mode 100644 rust/bioscript-formats/tests/fixtures/mini.cram create mode 100644 rust/bioscript-formats/tests/fixtures/mini.cram.crai create mode 100644 rust/bioscript-formats/tests/fixtures/mini.fa create mode 100644 rust/bioscript-formats/tests/fixtures/mini.fa.fai create mode 100644 rust/bioscript-formats/tests/fixtures/mini_bad_ref.fa create mode 100644 rust/bioscript-formats/tests/fixtures/mini_bad_ref.fa.fai create mode 100644 rust/vendor/lexical-util/.cargo-ok create mode 100644 rust/vendor/lexical-util/.cargo_vcs_info.json create mode 100644 rust/vendor/lexical-util/CODE_OF_CONDUCT.md create mode 100644 rust/vendor/lexical-util/Cargo.lock create mode 100644 rust/vendor/lexical-util/Cargo.toml create mode 100644 rust/vendor/lexical-util/Cargo.toml.orig create mode 100644 rust/vendor/lexical-util/LICENSE-APACHE create mode 100644 rust/vendor/lexical-util/LICENSE-MIT create mode 100644 rust/vendor/lexical-util/README.md create mode 100644 rust/vendor/lexical-util/src/algorithm.rs create mode 100644 rust/vendor/lexical-util/src/api.rs create mode 100644 rust/vendor/lexical-util/src/ascii.rs create mode 100644 rust/vendor/lexical-util/src/assert.rs create mode 100644 rust/vendor/lexical-util/src/bf16.rs create mode 100644 rust/vendor/lexical-util/src/constants.rs create mode 100644 rust/vendor/lexical-util/src/digit.rs create mode 100644 rust/vendor/lexical-util/src/div128.rs create mode 100644 rust/vendor/lexical-util/src/error.rs create mode 100644 rust/vendor/lexical-util/src/extended_float.rs create mode 100644 rust/vendor/lexical-util/src/f16.rs create mode 100644 rust/vendor/lexical-util/src/feature_format.rs create mode 100644 rust/vendor/lexical-util/src/format.rs create mode 100644 rust/vendor/lexical-util/src/format_builder.rs create mode 100644 rust/vendor/lexical-util/src/format_flags.rs create mode 100644 rust/vendor/lexical-util/src/iterator.rs create mode 100644 rust/vendor/lexical-util/src/lib.rs create mode 100644 rust/vendor/lexical-util/src/libm.rs create mode 100644 rust/vendor/lexical-util/src/mul.rs create mode 100644 rust/vendor/lexical-util/src/noskip.rs create mode 100644 rust/vendor/lexical-util/src/not_feature_format.rs create mode 100644 rust/vendor/lexical-util/src/num.rs create mode 100644 rust/vendor/lexical-util/src/options.rs create mode 100644 rust/vendor/lexical-util/src/prebuilt_formats.rs create mode 100644 rust/vendor/lexical-util/src/result.rs create mode 100644 rust/vendor/lexical-util/src/skip.rs create mode 100644 rust/vendor/lexical-util/src/step.rs create mode 100644 rust/vendor/lexical-util/tests/algorithm_tests.rs create mode 100644 rust/vendor/lexical-util/tests/ascii_tests.rs create mode 100644 rust/vendor/lexical-util/tests/bf16_tests.rs create mode 100644 rust/vendor/lexical-util/tests/digit_tests.rs create mode 100644 rust/vendor/lexical-util/tests/f16_tests.rs create mode 100644 rust/vendor/lexical-util/tests/feature_format_tests.rs create mode 100644 rust/vendor/lexical-util/tests/format_builder_tests.rs create mode 100644 rust/vendor/lexical-util/tests/format_flags_tests.rs create mode 100644 rust/vendor/lexical-util/tests/iterator_tests.rs create mode 100644 rust/vendor/lexical-util/tests/not_feature_format_tests.rs create mode 100644 rust/vendor/lexical-util/tests/num_tests.rs create mode 100644 rust/vendor/lexical-util/tests/skip_tests.rs diff --git a/.gitmodules b/.gitmodules index b427e0d..41b2bd7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "monty"] path = monty url = git@github.com:pydantic/monty.git +[submodule "noodles"] + path = noodles + url = git@github.com:madhavajay/noodles.git + branch = madhava/streaming-slice-records diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..d822646 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,97 @@ +# BioScript Architecture + +This document describes how bioscript is assembled and what each dependency does. It is intended for contributors who need to reason about runtime, genotype sourcing, and CRAM decoding. + +## Top-level Layout + +``` +bioscript/ + rust/ Rust workspace (the executable + core crates) + bioscript-cli/ binary entry point (`bs`) + bioscript-runtime/ orchestrates assay execution + timeouts + bioscript-formats/ genotype sourcing (txt, zip, vcf, cram) + bioscript-core/ shared types: VariantSpec, GenomicLocus, etc. + bioscript-schema/ validation of variant/catalogue YAML + bioscript-ffi/ C-ABI bindings for embedding + monty/ submodule — Pythonic language runtime + noodles/ submodule — bioinformatics I/O (our fork) + bs shell shim → `rust/target/…/bioscript` + assays/ user-authored assay scripts + variant YAMLs +``` + +Two submodules carry third-party code. Everything else is ours. + +## Dependencies + +### `monty` — Pythonic runtime + +- Upstream: `git@github.com:pydantic/monty.git` +- Vendored as a git submodule at `bioscript/monty` +- Provides the Python-like language that assay authors write (`classify_apol1()`, `count_char()`, etc.) +- Executed via `bioscript-runtime`, which owns the Monty interpreter and enforces per-assay time and memory budgets + +### `noodles` — CRAM / BAM / FASTA / CSI / CRAI + +- Upstream: `git@github.com:zaeleus/noodles.git` +- **We ship a fork** at `git@github.com:madhavajay/noodles.git`, branch `madhava/streaming-slice-records`, vendored as a submodule at `bioscript/noodles` +- Pulled into the build via `[patch.crates-io]` in `rust/Cargo.toml` — the registry versions of `noodles-cram`, `noodles-core`, `noodles-sam`, etc. are all redirected to the submodule so the crate graph stays unified + +The fork carries two upstreamable patches to `noodles-cram`: + +1. **`Slice::records_while(...)`** — a public streaming iterator over a CRAM slice's records with an early-termination callback. Upstream only exposes `Slice::records()` which returns `Vec`, forcing the entire slice (often ~10 000 records) to be decoded even for a single-base locus query. This was the root cause of the original apparent "hang" on chr22 APOL1 lookups. +2. **`validate_reference_md5` flag** — allows callers to opt out of the strict slice-level reference MD5 check when the supplied FASTA disagrees with the CRAM's encoding reference. Matches `samtools`'s default lenient behavior. + +Both changes also expose `Slice::header()` and `ReferenceSequenceContext` publicly, which upstream keeps `pub(crate)`. + +### `lexical-util` — vendored pin + +`rust/vendor/lexical-util` contains a checked-in copy of the `lexical-util` crate (a transitive dep of the Rust number-parsing stack). It is wired in via `[patch.crates-io]` in `rust/Cargo.toml`. Unlike `noodles`, this is *not* a fork with new functionality — it's a pin to a known-good source, isolating us from upstream churn. Treat it as read-only; if it ever needs refreshing, re-copy from a clean crates.io download and re-pin. + +## CRAM Read Path (`bioscript-formats/src/alignment.rs`) + +Given a single SNP or indel locus, the read path is: + +1. Build a `fasta::Repository` from the indexed reference FASTA. +2. Open the CRAM with its CRAI index. +3. **Filter CRAI entries** by reference sequence id + interval overlap. Only containers whose alignment spans touch the locus survive. +4. For each surviving container: `seek` to its byte offset, `read_container(...)`, then iterate its slices and skip any whose landmark is not in the selected set. +5. For each selected slice: + - `decode_blocks()` — decompresses the CRAM blocks once per slice. + - `records_while(..., validate_reference_md5 = true, on_record)` — streams records one at a time. For each record we construct an `AlignmentRecord` (start/end/sequence/CIGAR) and either skip it (outside the interval), forward it to the caller, or **stop** (once `record.start > locus.end`, since slices are coordinate-sorted). +6. On `reference sequence checksum mismatch`, the call is retried with `validate_reference_md5 = false`, a loud warning is written to `stderr`, and decoding proceeds. Results may be wrong at positions where the supplied FASTA actually differs from the encoding reference — the warning tells the user to investigate. + +Compared to calling upstream `Slice::records()` directly, the streaming path turns decoding ~10 000 records into decoding ~40 — roughly three orders of magnitude less work per locus. + +## Performance Expectations + +For a single SNP lookup on an aligned whole-genome CRAM: + +- `samtools view -T ref.fa file.cram region` — ~40 ms +- bioscript via this path — ~200 ms (three APOL1 loci total) +- previous `Slice::records()` path — >5 minutes, often killed by the assay timeout + +The integration tests in `bioscript-formats/tests/file_formats.rs` enforce a 5 s ceiling per single-locus CRAM lookup to catch regressions (e.g. if the streaming path silently breaks and we fall back to full-slice decoding). + +## Testing + +- `./test-assays.sh` — end-to-end: runs every assay against every input in `test-data/`, reports pass/fail + timings. +- `cargo test -p bioscript-formats --test file_formats` — integration tests including: + - `cram_apol1_snp_lookup_is_fast_and_correct` — correctness + wall-time budget + - `cram_md5_mismatch_is_tolerated_and_returns_correct_result` — asserts the fallback path produces a sensible genotype and a read depth within tolerance of `samtools mpileup` + +Tests skip gracefully when large test fixtures (the NYGC 1kG CRAM, GRCh38 FASTA) aren't present locally. + +## Updating the `noodles` Submodule + +When making changes to the noodles fork: + +```sh +cd bioscript/noodles +# edit, commit on madhava/streaming-slice-records +git push origin madhava/streaming-slice-records +cd .. +git add noodles +git commit -m "bump noodles submodule" +``` + +When upstream eventually accepts the streaming + MD5-bypass patches, drop the `[patch.crates-io]` entries in `rust/Cargo.toml`, remove the submodule, and depend on the registry version directly. diff --git a/lint.sh b/lint.sh index ebe7493..95fe95e 100755 --- a/lint.sh +++ b/lint.sh @@ -13,10 +13,15 @@ PACKAGES=( bioscript-schema ) -FMT_ARGS=() +PKG_ARGS=() for package in "${PACKAGES[@]}"; do - FMT_ARGS+=(-p "$package") + PKG_ARGS+=(-p "$package") done -cargo fmt --check "${FMT_ARGS[@]}" -cargo clippy --workspace --all-targets -- -D warnings +cargo fmt --check "${PKG_ARGS[@]}" + +filter_vendored() { + awk 'BEGIN{RS=""; ORS="\n\n"} !/\/noodles\/|\/vendor\/|`noodles-|`lexical-/' +} + +cargo clippy "${PKG_ARGS[@]}" --all-targets --color=never -- -D warnings 2> >(filter_vendored >&2) diff --git a/noodles b/noodles new file mode 160000 index 0000000..cbfb804 --- /dev/null +++ b/noodles @@ -0,0 +1 @@ +Subproject commit cbfb804255d08bbe085b7229c1bdc296ed49eeee diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 4b215a1..7be5391 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -100,39 +100,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "bindgen" -version = "0.69.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn", -] - -[[package]] -name = "bio-types" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dcf54f8b7f51450207d54780bab09c05f30b8b0caa991545082842e466ad7e" -dependencies = [ - "derive-new 0.6.0", - "lazy_static", - "regex", - "strum_macros 0.26.4", - "thiserror 1.0.69", -] - [[package]] name = "bioscript-cli" version = "0.1.0" @@ -167,7 +134,6 @@ version = "0.1.0" dependencies = [ "bioscript-core", "noodles", - "rust-htslib", "zip", ] @@ -193,7 +159,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec", + "bit-vec 0.8.0", ] [[package]] @@ -202,6 +168,15 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "2.11.0" @@ -220,6 +195,24 @@ dependencies = [ "wyz", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bstr" version = "1.12.1" @@ -270,13 +263,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] -name = "bzip2-sys" -version = "0.1.13+1.0.8" +name = "bzip2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" dependencies = [ - "cc", - "pkg-config", + "libbz2-rs-sys", ] [[package]] @@ -295,8 +287,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ "find-msvc-tools", - "jobserver", - "libc", "shlex", ] @@ -306,15 +296,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -335,26 +316,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "cmake" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" -dependencies = [ - "cc", -] - [[package]] name = "cobs" version = "0.3.0" @@ -394,12 +355,27 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -431,31 +407,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] -name = "curl-sys" -version = "0.4.87+curl-8.19.0" +name = "crypto-common" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a460380f0ef783703dcbe909107f39c162adeac050d73c850055118b5b6327" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "windows-sys 0.59.0", + "generic-array", + "typenum", ] [[package]] -name = "custom_derive" -version = "0.1.7" +name = "crypto-common" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +dependencies = [ + "hybrid-array", +] [[package]] -name = "derive-new" -version = "0.6.0" +name = "derive-where" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", @@ -463,10 +437,10 @@ dependencies = [ ] [[package]] -name = "derive-new" -version = "0.7.0" +name = "derive_arbitrary" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", @@ -474,25 +448,24 @@ dependencies = [ ] [[package]] -name = "derive-where" -version = "1.6.1" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "proc-macro2", - "quote", - "syn", + "block-buffer 0.10.4", + "crypto-common 0.1.7", ] [[package]] -name = "derive_arbitrary" -version = "1.4.2" +name = "digest" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" dependencies = [ - "proc-macro2", - "quote", - "syn", + "block-buffer 0.12.0", + "const-oid", + "crypto-common 0.2.1", ] [[package]] @@ -565,29 +538,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] -name = "form_urlencoded" -version = "1.2.2" +name = "funty" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] -name = "fs-utils" -version = "1.1.4" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc7a9dc005c944c98a935e7fd626faf5bf7e5a609f94bc13e42fc4a02e52593" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "quick-error", + "typenum", + "version_check", ] -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "get-size-derive2" version = "0.7.4" @@ -644,12 +609,6 @@ dependencies = [ "wasip2", ] -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - [[package]] name = "hash32" version = "0.2.1" @@ -684,7 +643,7 @@ checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", "hash32", - "rustc_version 0.4.1", + "rustc_version", "serde", "spin", "stable_deref_trait", @@ -697,20 +656,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "hts-sys" -version = "2.2.0" +name = "hybrid-array" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e38d7f1c121cd22aa214cb4dadd4277dc5447391eac518b899b29ba6356fbbb2" +checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" dependencies = [ - "bindgen", - "bzip2-sys", - "cc", - "curl-sys", - "fs-utils", - "glob", - "libz-sys", - "lzma-sys", - "openssl-sys", + "typenum", ] [[package]] @@ -737,115 +688,6 @@ dependencies = [ "cc", ] -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "ieee754" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9007da9cacbd3e6343da136e98b0d2df013f553d35bdec8b518f07bea768e19c" - [[package]] name = "indexmap" version = "2.14.0" @@ -876,15 +718,6 @@ dependencies = [ "syn", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -959,37 +792,28 @@ dependencies = [ "syn", ] -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "lazycell" -version = "1.3.0" +name = "lexical-core" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "0885f6cdfe75c96e45bbf1c4e49511f128201391ce3b56e60e29f5a1fadbc1c1" +dependencies = [ + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] [[package]] name = "lexical-parse-float" @@ -1013,55 +837,45 @@ dependencies = [ [[package]] name = "lexical-util" version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17" [[package]] -name = "libc" -version = "0.2.185" +name = "lexical-write-float" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" - -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +checksum = "c5afc668a27f460fb45a81a757b6bf2f43c2d7e30cb5a2dcd3abf294c78d62bd" dependencies = [ - "cfg-if", - "windows-link", + "lexical-util", + "lexical-write-integer", + "static_assertions", ] [[package]] -name = "libm" -version = "0.2.16" +name = "lexical-write-integer" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +checksum = "629ddff1a914a836fb245616a7888b62903aae58fa771e1d83943035efa0f978" +dependencies = [ + "lexical-util", + "static_assertions", +] [[package]] -name = "libz-sys" -version = "1.1.28" +name = "libbz2-rs-sys" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" -dependencies = [ - "cc", - "cmake", - "libc", - "pkg-config", - "vcpkg", -] +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" [[package]] -name = "linear-map" -version = "1.2.0" +name = "libc" +version = "0.2.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" +checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" [[package]] -name = "litemap" -version = "0.8.2" +name = "libm" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "lock_api" @@ -1079,14 +893,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] -name = "lzma-sys" -version = "0.1.20" +name = "lzma-rust2" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +checksum = "47bb1e988e6fb779cf720ad431242d3f03167c1b3f2b1aae7f1a94b2495b36ae" dependencies = [ - "cc", - "libc", - "pkg-config", + "sha2", ] [[package]] @@ -1113,16 +925,20 @@ dependencies = [ ] [[package]] -name = "memchr" -version = "2.8.0" +name = "md-5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" +dependencies = [ + "cfg-if", + "digest 0.11.2", +] [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "memchr" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "miniz_oxide" @@ -1136,7 +952,7 @@ dependencies = [ [[package]] name = "monty" -version = "0.0.9" +version = "0.0.11" dependencies = [ "ahash", "bytemuck", @@ -1144,14 +960,13 @@ dependencies = [ "fancy-regex", "hashbrown 0.16.1", "indexmap", - "itertools 0.14.0", + "itertools", "jiter", "libm", "num-bigint", "num-integer", "num-traits", "postcard", - "pyo3-build-config", "ruff_python_ast", "ruff_python_parser", "ruff_text_size", @@ -1162,44 +977,101 @@ dependencies = [ ] [[package]] -name = "newtype_derive" -version = "0.1.6" +name = "noodles" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8cd24d9f185bb7223958d8c1ff7a961b74b1953fd05dba7cc568a63b3861ec" +checksum = "4876a9caa25647b9ddc09883263b61fd6500ec7a4394f31daa5428026d5eb415" dependencies = [ - "rustc_version 0.1.7", + "noodles-bgzf", + "noodles-core", + "noodles-cram", + "noodles-csi", + "noodles-fasta", + "noodles-sam", ] [[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +name = "noodles-bam" +version = "0.88.0" dependencies = [ + "bstr", + "indexmap", "memchr", - "minimal-lexical", -] - -[[package]] -name = "noodles" -version = "0.104.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672be6c9bdafff24b6d176f5a79d8918d9733906abdf31be8a09aea5a57b75e" -dependencies = [ "noodles-bgzf", + "noodles-core", + "noodles-csi", + "noodles-sam", ] [[package]] name = "noodles-bgzf" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51c6a40480d211d9db28956b77443cac7c90b2d3d775f02e9c3fdf67ef8d611a" +version = "0.46.0" dependencies = [ "bytes", "crossbeam-channel", "flate2", ] +[[package]] +name = "noodles-core" +version = "0.19.0" +dependencies = [ + "bstr", +] + +[[package]] +name = "noodles-cram" +version = "0.92.0" +dependencies = [ + "bitflags", + "bstr", + "bzip2", + "flate2", + "indexmap", + "lexical-core", + "lzma-rust2", + "md-5", + "noodles-bam", + "noodles-core", + "noodles-fasta", + "noodles-sam", +] + +[[package]] +name = "noodles-csi" +version = "0.55.0" +dependencies = [ + "bit-vec 0.9.1", + "bstr", + "indexmap", + "noodles-bgzf", + "noodles-core", +] + +[[package]] +name = "noodles-fasta" +version = "0.60.0" +dependencies = [ + "bstr", + "memchr", + "noodles-bgzf", + "noodles-core", +] + +[[package]] +name = "noodles-sam" +version = "0.84.0" +dependencies = [ + "bitflags", + "bstr", + "indexmap", + "lexical-core", + "memchr", + "noodles-bgzf", + "noodles-core", + "noodles-csi", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1235,28 +1107,6 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -[[package]] -name = "openssl-src" -version = "300.6.0+3.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e8cbfd3a4a8c8f089147fd7aaa33cf8c7450c4d09f8f80698a0cf093abeff4" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.113" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad2f2c0eba47118757e4c6d2bff2838f3e0523380021356e7875e858372ce644" -dependencies = [ - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - [[package]] name = "ordermap" version = "1.2.0" @@ -1266,12 +1116,6 @@ dependencies = [ "indexmap", ] -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - [[package]] name = "phf" version = "0.11.3" @@ -1310,12 +1154,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pkg-config" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" - [[package]] name = "portable-atomic" version = "1.13.1" @@ -1335,15 +1173,6 @@ dependencies = [ "serde", ] -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1433,12 +1262,6 @@ dependencies = [ "syn", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.45" @@ -1512,18 +1335,6 @@ dependencies = [ "getrandom 0.2.17", ] -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - [[package]] name = "regex-automata" version = "0.4.14" @@ -1555,7 +1366,7 @@ dependencies = [ "ruff_python_trivia", "ruff_source_file", "ruff_text_size", - "rustc-hash 2.1.2", + "rustc-hash", "thiserror 2.0.18", ] @@ -1572,7 +1383,7 @@ dependencies = [ "ruff_python_ast", "ruff_python_trivia", "ruff_text_size", - "rustc-hash 2.1.2", + "rustc-hash", "static_assertions", "unicode-ident", "unicode-normalization", @@ -1584,7 +1395,7 @@ name = "ruff_python_trivia" version = "0.0.0" source = "git+https://github.com/astral-sh/ruff.git?rev=6ded4bed1651e30b34dd04cdaa50c763036abb0d#6ded4bed1651e30b34dd04cdaa50c763036abb0d" dependencies = [ - "itertools 0.14.0", + "itertools", "ruff_source_file", "ruff_text_size", "unicode-ident", @@ -1607,56 +1418,19 @@ dependencies = [ "get-size2", ] -[[package]] -name = "rust-htslib" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "354416dd2300ff9e7aff8ddc747c875d6c5086f83c2cb2599f3692421c2b77fd" -dependencies = [ - "bio-types", - "byteorder", - "custom_derive", - "derive-new 0.7.0", - "hts-sys", - "ieee754", - "lazy_static", - "libc", - "libz-sys", - "linear-map", - "newtype_derive", - "regex", - "thiserror 2.0.18", - "url", -] - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" -[[package]] -name = "rustc_version" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" -dependencies = [ - "semver 0.1.20", -] - [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.28", + "semver", ] [[package]] @@ -1686,12 +1460,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "semver" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" - [[package]] name = "semver" version = "1.0.28" @@ -1754,6 +1522,17 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1789,7 +1568,7 @@ checksum = "aba069c070b5e213f2a094deb7e5ed50ecb092be36102a4f4042e8d2056d060e" dependencies = [ "lexical-parse-float", "strum", - "strum_macros 0.27.2", + "strum_macros", ] [[package]] @@ -1819,20 +1598,7 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.27.2", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn", + "strum_macros", ] [[package]] @@ -1858,17 +1624,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tap" version = "1.0.1" @@ -1921,16 +1676,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - [[package]] name = "tinyvec" version = "1.11.0" @@ -1946,6 +1691,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1995,30 +1746,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.5" @@ -2052,9 +1779,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -2065,9 +1792,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2075,9 +1802,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", @@ -2088,9 +1815,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] @@ -2169,16 +1896,7 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2196,29 +1914,13 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -2227,102 +1929,48 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - [[package]] name = "wyz" version = "0.5.1" @@ -2332,29 +1980,6 @@ dependencies = [ "tap", ] -[[package]] -name = "yoke" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "zerocopy" version = "0.8.48" @@ -2375,60 +2000,6 @@ dependencies = [ "syn", ] -[[package]] -name = "zerofrom" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zip" version = "2.4.2" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index a3ddc8c..537e055 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -11,3 +11,13 @@ members = [ [profile.dev] opt-level = 2 + +[patch.crates-io] +lexical-util = { path = "vendor/lexical-util" } +noodles-bam = { path = "../noodles/noodles-bam" } +noodles-bgzf = { path = "../noodles/noodles-bgzf" } +noodles-core = { path = "../noodles/noodles-core" } +noodles-cram = { path = "../noodles/noodles-cram" } +noodles-csi = { path = "../noodles/noodles-csi" } +noodles-fasta = { path = "../noodles/noodles-fasta" } +noodles-sam = { path = "../noodles/noodles-sam" } diff --git a/rust/bioscript-ffi/src/lib.rs b/rust/bioscript-ffi/src/lib.rs index c64208e..2909d16 100644 --- a/rust/bioscript-ffi/src/lib.rs +++ b/rust/bioscript-ffi/src/lib.rs @@ -1,6 +1,7 @@ use std::{ env, ffi::{CStr, CString}, + fmt::Write as _, fs, os::raw::c_char, path::PathBuf, @@ -52,64 +53,33 @@ struct FfiResult { error: Option, } +/// Runs a bioscript file request described by a JSON-compatible Rust struct. +/// +/// # Errors +/// +/// Returns an error string when request parsing, optional index preparation, +/// runtime construction, script execution, or report writing fails. pub fn run_file_request(request: RunFileRequest) -> Result { let script_path = PathBuf::from(&request.script_path); - let runtime_root = match request.root { - Some(dir) => PathBuf::from(dir), - None => env::current_dir().map_err(|err| format!("failed to get current directory: {err}"))?, - }; - - let mut loader = GenotypeLoadOptions::default(); - if let Some(value) = request.input_format.as_deref() { - if value.eq_ignore_ascii_case("auto") { - loader.format = None; - } else { - let parsed = value - .parse::() - .map_err(|err| format!("invalid inputFormat value {value}: {err}"))?; - loader.format = Some(parsed); - } - } - loader.input_index = request.input_index.map(PathBuf::from); - loader.reference_file = request.reference_file.map(PathBuf::from); - loader.reference_index = request.reference_index.map(PathBuf::from); - - let mut limits = ResourceLimits::new() - .max_duration(Duration::from_millis(100)) - .max_memory(8 * 1024 * 1024) - .max_allocations(200_000) - .gc_interval(1000) - .max_recursion_depth(Some(200)); - - if let Some(value) = request.max_duration_ms { - limits = limits.max_duration(Duration::from_millis(value)); - } - if let Some(value) = request.max_memory_bytes { - limits = limits.max_memory(value); - } - if let Some(value) = request.max_allocations { - limits = limits.max_allocations(value); - } - if let Some(value) = request.max_recursion_depth { - limits = limits.max_recursion_depth(Some(value)); - } + let runtime_root = runtime_root(&request)?; + let mut loader = build_loader(&request)?; + let limits = build_limits(&request); let mut ffi_timings: Vec = Vec::new(); if request.auto_index.unwrap_or(false) { let auto_index_started = Instant::now(); let cwd = env::current_dir().map_err(|err| format!("failed to get cwd: {err}"))?; - let effective_cache = request - .cache_dir - .as_ref() - .map(PathBuf::from) - .map(|path| { + let effective_cache = request.cache_dir.as_ref().map_or_else( + || runtime_root.join(".bioscript-cache"), + |dir| { + let path = PathBuf::from(dir); if path.is_absolute() { path } else { runtime_root.join(path) } - }) - .unwrap_or_else(|| runtime_root.join(".bioscript-cache")); + }, + ); let prepare_request = PrepareRequest { root: runtime_root.clone(), cwd: cwd.clone(), @@ -142,11 +112,8 @@ pub fn run_file_request(request: RunFileRequest) -> Result Result Result Result { + match request.root.as_deref() { + Some(dir) => Ok(PathBuf::from(dir)), + None => env::current_dir().map_err(|err| format!("failed to get current directory: {err}")), + } +} + +fn build_loader(request: &RunFileRequest) -> Result { + let mut loader = GenotypeLoadOptions::default(); + if let Some(value) = request.input_format.as_deref() { + if value.eq_ignore_ascii_case("auto") { + loader.format = None; + } else { + let parsed = value + .parse::() + .map_err(|err| format!("invalid inputFormat value {value}: {err}"))?; + loader.format = Some(parsed); + } + } + loader.input_index = request.input_index.clone().map(PathBuf::from); + loader.reference_file = request.reference_file.clone().map(PathBuf::from); + loader.reference_index = request.reference_index.clone().map(PathBuf::from); + Ok(loader) +} + +fn build_limits(request: &RunFileRequest) -> ResourceLimits { + let mut limits = ResourceLimits::new() + .max_duration(Duration::from_millis(100)) + .max_memory(8 * 1024 * 1024) + .max_allocations(200_000) + .gc_interval(1000) + .max_recursion_depth(Some(200)); + + if let Some(value) = request.max_duration_ms { + limits = limits.max_duration(Duration::from_millis(value)); + } + if let Some(value) = request.max_memory_bytes { + limits = limits.max_memory(value); + } + if let Some(value) = request.max_allocations { + limits = limits.max_allocations(value); + } + if let Some(value) = request.max_recursion_depth { + limits = limits.max_recursion_depth(Some(value)); + } + + limits +} + fn write_timing_report(path: &PathBuf, timings: &[StageTiming]) -> Result<(), String> { if let Some(parent) = path.parent() { - fs::create_dir_all(parent) - .map_err(|err| format!("failed to create timing report dir {}: {err}", parent.display()))?; + fs::create_dir_all(parent).map_err(|err| { + format!( + "failed to create timing report dir {}: {err}", + parent.display() + ) + })?; } let mut output = String::from("stage\tduration_ms\tdetail\n"); for timing in timings { - output.push_str(&format!( + let _ = writeln!( + output, "{}\t{}\t{}\n", timing.stage, timing.duration_ms, timing.detail.replace('\t', " ") - )); + ); } - fs::write(path, output).map_err(|err| format!("failed to write timing report {}: {err}", path.display())) + fs::write(path, output) + .map_err(|err| format!("failed to write timing report {}: {err}", path.display())) } #[unsafe(no_mangle)] +/// Executes a bioscript run request encoded as a UTF-8 JSON C string. +/// +/// # Safety +/// +/// `request_json` must either be null or point to a valid, NUL-terminated C +/// string that remains alive for the duration of this call. pub unsafe extern "C" fn bioscript_run_file_json(request_json: *const c_char) -> *mut c_char { let response = unsafe { if request_json.is_null() { @@ -242,6 +273,12 @@ pub unsafe extern "C" fn bioscript_run_file_json(request_json: *const c_char) -> } #[unsafe(no_mangle)] +/// Frees a string previously returned by [`bioscript_run_file_json`]. +/// +/// # Safety +/// +/// `ptr` must be null or a pointer returned by [`CString::into_raw`] from this +/// library, and it must not be freed more than once. pub unsafe extern "C" fn bioscript_free_string(ptr: *mut c_char) { if !ptr.is_null() { unsafe { @@ -290,6 +327,7 @@ pub mod android { } }; - env.new_string(response).expect("jni new_string should succeed") + env.new_string(response) + .expect("jni new_string should succeed") } } diff --git a/rust/bioscript-formats/Cargo.toml b/rust/bioscript-formats/Cargo.toml index 9b2fb67..be17e43 100644 --- a/rust/bioscript-formats/Cargo.toml +++ b/rust/bioscript-formats/Cargo.toml @@ -8,11 +8,8 @@ crate-type = ["rlib"] [dependencies] bioscript-core = { path = "../bioscript-core" } -noodles = { version = "0.104.0", features = ["bgzf"] } +noodles = { version = "0.109.0", features = ["bgzf", "core", "cram", "csi", "fasta", "sam"] } zip = { version = "2.2.0", default-features = false, features = ["deflate"] } -[target.'cfg(not(any(target_os = "ios", target_os = "tvos")))'.dependencies] -rust-htslib = "0.51.0" - [lints.clippy] pedantic = { level = "warn", priority = -1 } diff --git a/rust/bioscript-formats/src/alignment.rs b/rust/bioscript-formats/src/alignment.rs new file mode 100644 index 0000000..e2345da --- /dev/null +++ b/rust/bioscript-formats/src/alignment.rs @@ -0,0 +1,548 @@ +use std::{ + collections::{BTreeMap, HashSet}, + io::Seek as _, + path::Path, +}; + +use noodles::{ + core::{Position, Region}, + cram::{self, crai, io::reader::Container}, + fasta::{self, repository::adapters::IndexedReader as FastaIndexedReader}, + sam::{ + self, + alignment::{ + Record as _, + record::{Cigar as _, Sequence as _}, + }, + }, +}; + +use bioscript_core::{GenomicLocus, RuntimeError}; + +use crate::genotype::GenotypeLoadOptions; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum AlignmentOpKind { + Match, + Insertion, + Deletion, + Skip, + SoftClip, + HardClip, + Pad, + SequenceMatch, + SequenceMismatch, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct AlignmentOp { + pub kind: AlignmentOpKind, + pub len: usize, +} + +#[derive(Debug, Clone)] +pub(crate) struct AlignmentRecord { + pub start: i64, + pub end: i64, + pub is_unmapped: bool, + pub sequence: Vec, + pub cigar: Vec, +} + +#[derive(Debug, Clone)] +struct SelectedContainer { + offset: u64, + landmarks: HashSet, +} + +pub(crate) fn for_each_cram_record( + path: &Path, + options: &GenotypeLoadOptions, + reference_file: &Path, + locus: &GenomicLocus, + mut on_record: F, +) -> Result<(), RuntimeError> +where + F: FnMut(AlignmentRecord) -> Result, +{ + let repository = build_reference_repository(reference_file)?; + let mut builder = + cram::io::indexed_reader::Builder::default().set_reference_sequence_repository(repository); + + if let Some(index_path) = options.input_index.as_ref() { + let index = crai::fs::read(index_path).map_err(|err| { + RuntimeError::Io(format!( + "failed to read CRAM index {} for {}: {err}", + index_path.display(), + path.display() + )) + })?; + builder = builder.set_index(index); + } + + let mut reader = builder.build_from_path(path).map_err(|err| { + RuntimeError::Io(format!( + "failed to open indexed CRAM {}: {err}", + path.display() + )) + })?; + + let header = reader.read_header().map_err(|err| { + RuntimeError::Io(format!( + "failed to read CRAM header {}: {err}", + path.display() + )) + })?; + + let region = build_region(&header, locus).ok_or_else(|| { + RuntimeError::Unsupported(format!( + "indexed CRAM does not contain contig {} for {}:{}-{}", + locus.chrom, locus.chrom, locus.start, locus.end + )) + })?; + + let selected_containers = select_query_containers(reader.index(), &header, ®ion)?; + + stream_selected_containers( + path, + &mut reader, + &header, + ®ion, + locus.end, + &selected_containers, + &mut on_record, + )?; + + Ok(()) +} + +pub(crate) fn query_cram_records( + path: &Path, + options: &GenotypeLoadOptions, + reference_file: &Path, + locus: &GenomicLocus, +) -> Result, RuntimeError> { + let mut records = Vec::new(); + for_each_cram_record(path, options, reference_file, locus, |record| { + records.push(record); + Ok(true) + })?; + Ok(records) +} + +fn build_reference_repository(reference_file: &Path) -> Result { + let reader = fasta::io::indexed_reader::Builder::default() + .build_from_path(reference_file) + .map_err(|err| { + RuntimeError::Io(format!( + "failed to open indexed FASTA {}: {err}", + reference_file.display() + )) + })?; + + Ok(fasta::Repository::new(FastaIndexedReader::new(reader))) +} + +fn build_region(header: &sam::Header, locus: &GenomicLocus) -> Option { + let chrom = resolve_reference_name(header, &locus.chrom)?; + let start = Position::try_from(usize::try_from(locus.start).ok()?).ok()?; + let end = Position::try_from(usize::try_from(locus.end).ok()?).ok()?; + let raw = format!("{chrom}:{start}-{end}"); + raw.parse().ok() +} + +fn select_query_containers( + index: &crai::Index, + header: &sam::Header, + region: &Region, +) -> Result, RuntimeError> { + let reference_sequence_id = + resolve_reference_sequence_id(header, region.name()).ok_or_else(|| { + RuntimeError::Unsupported(format!( + "indexed CRAM does not contain contig {}", + String::from_utf8_lossy(region.name()) + )) + })?; + + let interval = region.interval(); + let mut containers = BTreeMap::>::new(); + + for record in index { + if record.reference_sequence_id() != Some(reference_sequence_id) { + continue; + } + + if !record_intersects_interval(record, interval) { + continue; + } + + containers + .entry(record.offset()) + .or_default() + .insert(record.landmark()); + } + + Ok(containers + .into_iter() + .map(|(offset, landmarks)| SelectedContainer { offset, landmarks }) + .collect()) +} + +fn stream_selected_containers( + path: &Path, + reader: &mut cram::io::indexed_reader::IndexedReader, + header: &sam::Header, + region: &Region, + locus_end: i64, + selected_containers: &[SelectedContainer], + on_record: &mut F, +) -> Result<(), RuntimeError> +where + F: FnMut(AlignmentRecord) -> Result, +{ + let interval = region.interval(); + + for selected_container in selected_containers { + let offset = selected_container.offset; + reader + .get_mut() + .seek(std::io::SeekFrom::Start(offset)) + .map_err(|err| { + RuntimeError::Io(format!( + "failed to seek CRAM container at offset {offset} in {}: {err}", + path.display() + )) + })?; + + let mut container = Container::default(); + let len = reader.read_container(&mut container).map_err(|err| { + RuntimeError::Io(format!( + "failed to read CRAM container at offset {offset} in {}: {err}", + path.display() + )) + })?; + + if len == 0 { + break; + } + + let compression_header = container.compression_header().map_err(|err| { + RuntimeError::Io(format!( + "failed to decode CRAM compression header from {}: {err}", + path.display() + )) + })?; + + let landmarks = container.header().landmarks().to_vec(); + let reference_sequence_repository = reader.reference_sequence_repository().clone(); + + let mut stop = false; + + for (index, slice_result) in container.slices().enumerate() { + let slice = slice_result.map_err(|err| { + RuntimeError::Io(format!( + "failed to read CRAM slice from {}: {err}", + path.display() + )) + })?; + + let Some(&landmark_i32) = landmarks.get(index) else { + return Err(RuntimeError::Io(format!( + "missing CRAM slice landmark {} in {}", + index, + path.display() + ))); + }; + let Ok(landmark) = u64::try_from(landmark_i32) else { + continue; + }; + if !selected_container.landmarks.contains(&landmark) { + continue; + } + + let (core_data_src, external_data_srcs) = slice.decode_blocks().map_err(|err| { + RuntimeError::Io(format!( + "failed to decode CRAM slice blocks from {}: {err}", + path.display() + )) + })?; + + let mut callback_err: Option = None; + let decode_result = slice.records_while( + reference_sequence_repository.clone(), + header, + &compression_header, + &core_data_src, + &external_data_srcs, + true, + |record| { + let alignment_record = match build_alignment_record_from_cram(path, record) { + Ok(r) => r, + Err(e) => { + callback_err = Some(e); + return Ok(false); + } + }; + + if alignment_record.start > locus_end { + stop = true; + return Ok(false); + } + + if !alignment_record_intersects_interval(&alignment_record, interval) { + return Ok(true); + } + + match on_record(alignment_record) { + Ok(true) => Ok(true), + Ok(false) => { + stop = true; + Ok(false) + } + Err(e) => { + callback_err = Some(e); + Ok(false) + } + } + }, + ); + + match decode_result { + Ok(()) => {} + Err(err) if is_reference_md5_mismatch(&err) => { + eprintln!( + "[bioscript] warning: CRAM reference MD5 mismatch for {} slice landmark {} — \ + retrying without checksum validation. Results may be incorrect if the \ + supplied reference differs from the one used to encode this CRAM. \ + Details: {}", + path.display(), + landmark, + err + ); + callback_err = None; + stop = false; + slice + .records_while( + reference_sequence_repository.clone(), + header, + &compression_header, + &core_data_src, + &external_data_srcs, + false, + |record| { + let alignment_record = + match build_alignment_record_from_cram(path, record) { + Ok(r) => r, + Err(e) => { + callback_err = Some(e); + return Ok(false); + } + }; + + if alignment_record.start > locus_end { + stop = true; + return Ok(false); + } + + if !alignment_record_intersects_interval( + &alignment_record, + interval, + ) { + return Ok(true); + } + + match on_record(alignment_record) { + Ok(true) => Ok(true), + Ok(false) => { + stop = true; + Ok(false) + } + Err(e) => { + callback_err = Some(e); + Ok(false) + } + } + }, + ) + .map_err(|err| { + RuntimeError::Io(format!( + "failed to decode CRAM slice records from {} (unchecked): {err}", + path.display() + )) + })?; + } + Err(err) => { + return Err(RuntimeError::Io(format!( + "failed to decode CRAM slice records from {}: {err}", + path.display() + ))); + } + } + + if let Some(err) = callback_err { + return Err(err); + } + + if stop { + break; + } + } + + if stop { + break; + } + } + + Ok(()) +} + +fn is_reference_md5_mismatch(err: &std::io::Error) -> bool { + err.to_string() + .contains("reference sequence checksum mismatch") +} + +fn build_alignment_record_from_cram( + path: &Path, + record: &cram::Record<'_>, +) -> Result { + let flags = record.flags().map_err(|err| { + RuntimeError::Io(format!( + "failed to read CRAM record flags from {}: {err}", + path.display() + )) + })?; + let is_unmapped = flags.is_unmapped(); + + let start = match record.alignment_start() { + Some(Ok(pos)) => i64::try_from(usize::from(pos)).map_err(|_| { + RuntimeError::Unsupported(format!( + "record alignment start exceeds i64 range in {}", + path.display() + )) + })?, + Some(Err(err)) => { + return Err(RuntimeError::Io(format!( + "failed to read CRAM alignment_start from {}: {err}", + path.display() + ))); + } + None => 0, + }; + + let end = match record.alignment_end() { + Some(Ok(pos)) => i64::try_from(usize::from(pos)).map_err(|_| { + RuntimeError::Unsupported(format!( + "record alignment end exceeds i64 range in {}", + path.display() + )) + })?, + Some(Err(err)) => { + return Err(RuntimeError::Io(format!( + "failed to read CRAM alignment_end from {}: {err}", + path.display() + ))); + } + None => start, + }; + + let sequence: Vec = record.sequence().iter().collect(); + let cigar = record + .cigar() + .iter() + .map(|result| { + result.map(map_op).map_err(|err| { + RuntimeError::Io(format!( + "failed to read record CIGAR from {}: {err}", + path.display() + )) + }) + }) + .collect::, _>>()?; + + Ok(AlignmentRecord { + start, + end, + is_unmapped, + sequence, + cigar, + }) +} + +fn resolve_reference_sequence_id(header: &sam::Header, name: &[u8]) -> Option { + header + .reference_sequences() + .iter() + .position(|(candidate, _)| { + let candidate_name: &[u8] = candidate.as_ref(); + candidate_name == name + }) +} + +fn record_intersects_interval( + record: &crai::Record, + interval: noodles::core::region::Interval, +) -> bool { + let Some(start) = record.alignment_start() else { + return false; + }; + + if record.alignment_span() == 0 { + return false; + } + + let Some(end) = start.checked_add(record.alignment_span() - 1) else { + return false; + }; + + interval.intersects((start..=end).into()) +} + +fn alignment_record_intersects_interval( + record: &AlignmentRecord, + interval: noodles::core::region::Interval, +) -> bool { + let Ok(start) = usize::try_from(record.start).and_then(Position::try_from) else { + return false; + }; + let Ok(end) = usize::try_from(record.end).and_then(Position::try_from) else { + return false; + }; + + interval.intersects((start..=end).into()) +} + +fn resolve_reference_name(header: &sam::Header, chrom: &str) -> Option { + let candidates = [ + chrom.to_owned(), + format!("chr{chrom}"), + chrom.trim_start_matches("chr").to_owned(), + ]; + + candidates.into_iter().find(|candidate| { + header.reference_sequences().iter().any(|(name, _)| { + let name_bytes: &[u8] = name.as_ref(); + name_bytes == candidate.as_bytes() + }) + }) +} + +fn map_op(op: sam::alignment::record::cigar::Op) -> AlignmentOp { + use sam::alignment::record::cigar::op::Kind; + + let kind = match op.kind() { + Kind::Match => AlignmentOpKind::Match, + Kind::Insertion => AlignmentOpKind::Insertion, + Kind::Deletion => AlignmentOpKind::Deletion, + Kind::Skip => AlignmentOpKind::Skip, + Kind::SoftClip => AlignmentOpKind::SoftClip, + Kind::HardClip => AlignmentOpKind::HardClip, + Kind::Pad => AlignmentOpKind::Pad, + Kind::SequenceMatch => AlignmentOpKind::SequenceMatch, + Kind::SequenceMismatch => AlignmentOpKind::SequenceMismatch, + }; + + AlignmentOp { + kind, + len: op.len(), + } +} diff --git a/rust/bioscript-formats/src/genotype.rs b/rust/bioscript-formats/src/genotype.rs index e0db886..eed1489 100644 --- a/rust/bioscript-formats/src/genotype.rs +++ b/rust/bioscript-formats/src/genotype.rs @@ -8,14 +8,14 @@ use std::{ }; use noodles::bgzf; -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] -use rust_htslib::bam::{self, Read}; use zip::ZipArchive; use bioscript_core::{ Assembly, GenomicLocus, RuntimeError, VariantKind, VariantObservation, VariantSpec, }; +use crate::alignment::{self, AlignmentOpKind, AlignmentRecord}; + const COMMENT_PREFIXES: [&str; 2] = ["#", "//"]; const RSID_ALIASES: &[&str] = &["rsid", "name", "snp", "marker", "id", "snpid"]; @@ -68,7 +68,6 @@ struct DelimitedBackend { zip_entry_name: Option, } -#[cfg_attr(any(target_os = "ios", target_os = "tvos"), allow(dead_code))] #[derive(Debug, Clone)] struct CramBackend { path: PathBuf, @@ -263,7 +262,7 @@ impl GenotypeStore { }, QueryBackend::Cram(_) => BackendCapabilities { rsid_lookup: false, - locus_lookup: cfg!(not(any(target_os = "ios", target_os = "tvos"))), + locus_lookup: true, }, } } @@ -396,7 +395,6 @@ impl DelimitedBackend { } } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] impl CramBackend { fn backend_name(&self) -> &'static str { "cram" @@ -435,7 +433,10 @@ impl CramBackend { VariantKind::Deletion => { self.observe_deletion(variant, assembly, &locus, reference_file)? } - VariantKind::Insertion | VariantKind::Indel | VariantKind::Other => { + VariantKind::Insertion | VariantKind::Indel => { + self.observe_indel(variant, assembly, &locus, reference_file)? + } + VariantKind::Other => { return Err(RuntimeError::Unsupported(format!( "backend '{}' does not yet support {:?} observation for {}", self.backend_name(), @@ -475,23 +476,17 @@ impl CramBackend { let mut ref_count = 0u32; let mut depth = 0u32; - self.with_pileups(reference_file, locus, |pileup| { - let pos1 = i64::from(pileup.pos()) + 1; - if pos1 != target_pos { - return; - } - - for alignment in pileup.alignments() { - if alignment.is_del() || alignment.is_refskip() { - continue; + alignment::for_each_cram_record( + &self.path, + &self.options, + reference_file, + locus, + |record| { + if record.is_unmapped { + return Ok(true); } - let Some(qpos) = alignment.qpos() else { - continue; - }; - let record = alignment.record(); - let bases = record.seq().as_bytes(); - let Some(base) = bases.get(qpos).copied() else { - continue; + let Some(base) = base_at_position(&record, target_pos) else { + return Ok(true); }; let base = (base as char).to_ascii_uppercase(); depth += 1; @@ -500,8 +495,9 @@ impl CramBackend { } else if base == alternate { alt_count += 1; } - } - })?; + Ok(true) + }, + )?; Ok(VariantObservation { backend: self.backend_name().to_owned(), @@ -536,29 +532,25 @@ impl CramBackend { let mut ref_count = 0u32; let mut depth = 0u32; - self.with_pileups(reference_file, &anchor_window(locus), |pileup| { - let pos1 = i64::from(pileup.pos()) + 1; - if pos1 != anchor_pos { - return; - } - - for alignment in pileup.alignments() { - if alignment.is_refskip() { - continue; + alignment::for_each_cram_record( + &self.path, + &self.options, + reference_file, + &anchor_window(locus), + |record| { + if record.is_unmapped || !spans_position(&record, anchor_pos) { + return Ok(true); } depth += 1; - match alignment.indel() { - bam::pileup::Indel::Del(len) - if usize::try_from(len).ok() == Some(deletion_length) => - { + match indel_at_anchor(&record, anchor_pos) { + Some((AlignmentOpKind::Deletion, len)) if len == deletion_length => { alt_count += 1; } - _ => { - ref_count += 1; - } + _ => ref_count += 1, } - } - })?; + Ok(true) + }, + )?; Ok(VariantObservation { backend: self.backend_name().to_owned(), @@ -577,135 +569,84 @@ impl CramBackend { }) } - fn with_pileups( + fn observe_indel( &self, - reference_file: &Path, + variant: &VariantSpec, + assembly: Assembly, locus: &GenomicLocus, - mut on_pileup: F, - ) -> Result<(), RuntimeError> - where - F: FnMut(&bam::pileup::Pileup), - { - if let Some(index_path) = self.options.input_index.as_ref() { - let mut reader = bam::IndexedReader::from_path_and_index(&self.path, index_path) - .map_err(|err| { - RuntimeError::Io(format!( - "failed to open indexed CRAM {} with index {}: {err}", - self.path.display(), - index_path.display() - )) - })?; - reader.set_reference(reference_file).map_err(|err| { - RuntimeError::Io(format!( - "failed to set CRAM reference {} for {}: {err}", - reference_file.display(), - self.path.display() - )) - })?; - fetch_locus(&mut reader, locus)?; - for pileup in reader.pileup() { - let pileup = pileup.map_err(|err| { - RuntimeError::Io(format!( - "failed while piling up {}: {err}", - self.path.display() - )) - })?; - on_pileup(&pileup); - } - return Ok(()); - } - - if self.path.with_extension("cram.crai").exists() - || self.path.with_extension("crai").exists() - { - let mut reader = bam::IndexedReader::from_path(&self.path).map_err(|err| { - RuntimeError::Io(format!( - "failed to open indexed CRAM {}: {err}", - self.path.display() - )) - })?; - reader.set_reference(reference_file).map_err(|err| { - RuntimeError::Io(format!( - "failed to set CRAM reference {} for {}: {err}", - reference_file.display(), - self.path.display() - )) - })?; - fetch_locus(&mut reader, locus)?; - for pileup in reader.pileup() { - let pileup = pileup.map_err(|err| { - RuntimeError::Io(format!( - "failed while piling up {}: {err}", - self.path.display() - )) - })?; - on_pileup(&pileup); - } - return Ok(()); - } - - let mut reader = bam::Reader::from_path(&self.path).map_err(|err| { - RuntimeError::Io(format!( - "failed to open CRAM {}: {err}", - self.path.display() - )) + reference_file: &Path, + ) -> Result { + let reference = variant.reference.clone().ok_or_else(|| { + RuntimeError::InvalidArguments("indel variant requires ref/reference".to_owned()) })?; - reader.set_reference(reference_file).map_err(|err| { - RuntimeError::Io(format!( - "failed to set CRAM reference {} for {}: {err}", - reference_file.display(), - self.path.display() - )) + let alternate = variant.alternate.clone().ok_or_else(|| { + RuntimeError::InvalidArguments("indel variant requires alt/alternate".to_owned()) })?; + let records = + alignment::query_cram_records(&self.path, &self.options, reference_file, locus)?; - let target_tid = header_tid(reader.header(), &locus.chrom).ok_or_else(|| { - RuntimeError::Unsupported(format!( - "reference {} does not contain contig {} for {}", - self.path.display(), - locus.chrom, - describe_locus(locus) - )) - })?; + let mut alt_count = 0u32; + let mut ref_count = 0u32; + let mut depth = 0u32; + let mut matching_alt_lengths = BTreeSet::new(); - for pileup in reader.pileup() { - let pileup = pileup.map_err(|err| { - RuntimeError::Io(format!( - "failed while piling up {}: {err}", - self.path.display() - )) - })?; - if pileup.tid() != target_tid { + for record in records { + if record.is_unmapped { continue; } - let pos1 = i64::from(pileup.pos()) + 1; - if pos1 < locus.start { + if !record_overlaps_locus(&record, locus) { continue; } - if pos1 > locus.end { - break; + let classification = + classify_expected_indel(&record, locus, reference.len(), &alternate)?; + if !classification.covering { + continue; + } + depth += 1; + if classification.matches_alt { + alt_count += 1; + matching_alt_lengths.insert(classification.observed_len); + } else if classification.reference_like { + ref_count += 1; } - on_pileup(&pileup); } - Ok(()) - } -} - -#[cfg(any(target_os = "ios", target_os = "tvos"))] -impl CramBackend { - fn backend_name(&self) -> &'static str { - "cram" - } + let evidence_label = if matching_alt_lengths.is_empty() { + "none".to_owned() + } else { + matching_alt_lengths + .into_iter() + .map(|len| len.to_string()) + .collect::>() + .join(",") + }; - fn lookup_variant(&self, _variant: &VariantSpec) -> Result { - Err(RuntimeError::Unsupported( - "CRAM/BAM-backed lookup is not supported on Apple mobile targets".to_owned(), - )) + Ok(VariantObservation { + backend: self.backend_name().to_owned(), + matched_rsid: variant.rsids.first().cloned(), + assembly: Some(assembly), + genotype: infer_copy_number_genotype( + &reference, &alternate, ref_count, alt_count, depth, + ), + ref_count: Some(ref_count), + alt_count: Some(alt_count), + depth: Some(depth), + evidence: vec![format!( + "observed indel at {} depth={} ref_count={} alt_count={} matching_alt_lengths={}", + describe_locus(locus), + depth, + ref_count, + alt_count, + evidence_label + )], + }) } } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] -fn choose_variant_locus(variant: &VariantSpec, reference_file: &Path) -> Option<(Assembly, GenomicLocus)> { +fn choose_variant_locus( + variant: &VariantSpec, + reference_file: &Path, +) -> Option<(Assembly, GenomicLocus)> { match detect_reference_assembly(reference_file) { Some(Assembly::Grch38) => variant .grch38 @@ -740,7 +681,6 @@ fn choose_variant_locus(variant: &VariantSpec, reference_file: &Path) -> Option< } } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] fn detect_reference_assembly(reference_file: &Path) -> Option { let lower = reference_file.to_string_lossy().to_ascii_lowercase(); if lower.contains("grch38") || lower.contains("hg38") || lower.contains("assembly38") { @@ -752,50 +692,10 @@ fn detect_reference_assembly(reference_file: &Path) -> Option { } } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] -fn fetch_locus(reader: &mut bam::IndexedReader, locus: &GenomicLocus) -> Result<(), RuntimeError> { - let tid = header_tid(reader.header(), &locus.chrom).ok_or_else(|| { - RuntimeError::Unsupported(format!( - "indexed CRAM does not contain contig {} for {}", - locus.chrom, - describe_locus(locus) - )) - })?; - - let start = locus.start.saturating_sub(1); - let end = locus.end; - let tid = i32::try_from(tid).map_err(|_| { - RuntimeError::Unsupported(format!( - "indexed CRAM contig id {tid} does not fit i32 for {}", - describe_locus(locus) - )) - })?; - reader.fetch((tid, start, end)).map_err(|err| { - RuntimeError::Io(format!( - "failed to fetch {}:{}-{}: {err}", - locus.chrom, locus.start, locus.end - )) - }) -} - -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] -fn header_tid(header: &bam::HeaderView, chrom: &str) -> Option { - let candidates = [ - chrom.to_owned(), - format!("chr{chrom}"), - chrom.trim_start_matches("chr").to_owned(), - ]; - candidates - .iter() - .find_map(|candidate| header.tid(candidate.as_bytes())) -} - -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] fn describe_locus(locus: &GenomicLocus) -> String { format!("{}:{}-{}", locus.chrom, locus.start, locus.end) } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] fn anchor_window(locus: &GenomicLocus) -> GenomicLocus { let anchor = locus.start.saturating_sub(1); GenomicLocus { @@ -805,7 +705,6 @@ fn anchor_window(locus: &GenomicLocus) -> GenomicLocus { } } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] fn first_base(value: &str) -> Option { value .trim() @@ -814,7 +713,6 @@ fn first_base(value: &str) -> Option { .map(|ch| ch.to_ascii_uppercase()) } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] fn infer_snp_genotype( reference: char, alternate: char, @@ -835,7 +733,6 @@ fn infer_snp_genotype( } } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] fn infer_copy_number_genotype( reference: &str, alternate: &str, @@ -856,6 +753,138 @@ fn infer_copy_number_genotype( } } +#[derive(Debug, Clone, Copy)] +struct IndelClassification { + covering: bool, + reference_like: bool, + matches_alt: bool, + observed_len: usize, +} + +fn len_as_i64(len: usize) -> Option { + i64::try_from(len).ok() +} + +fn base_at_position(record: &AlignmentRecord, target_pos: i64) -> Option { + let mut ref_pos = record.start; + let mut read_pos = 0usize; + + for op in &record.cigar { + match op.kind { + AlignmentOpKind::Match + | AlignmentOpKind::SequenceMatch + | AlignmentOpKind::SequenceMismatch => { + let op_len = len_as_i64(op.len)?; + if target_pos >= ref_pos && target_pos < ref_pos + op_len { + let offset = usize::try_from(target_pos - ref_pos).ok()?; + return record.sequence.get(read_pos + offset).copied(); + } + ref_pos += op_len; + read_pos += op.len; + } + AlignmentOpKind::Insertion | AlignmentOpKind::SoftClip => { + read_pos += op.len; + } + AlignmentOpKind::Deletion | AlignmentOpKind::Skip => { + let op_len = len_as_i64(op.len)?; + if target_pos >= ref_pos && target_pos < ref_pos + op_len { + return None; + } + ref_pos += op_len; + } + AlignmentOpKind::HardClip | AlignmentOpKind::Pad => {} + } + } + + None +} + +fn spans_position(record: &AlignmentRecord, pos: i64) -> bool { + pos >= record.start.saturating_sub(1) && pos <= record.end +} + +fn record_overlaps_locus(record: &AlignmentRecord, locus: &GenomicLocus) -> bool { + record.end >= locus.start && record.start <= locus.end +} + +fn indel_at_anchor(record: &AlignmentRecord, anchor_pos: i64) -> Option<(AlignmentOpKind, usize)> { + let mut ref_pos = record.start; + + for op in &record.cigar { + match op.kind { + AlignmentOpKind::Match + | AlignmentOpKind::SequenceMatch + | AlignmentOpKind::SequenceMismatch + | AlignmentOpKind::Skip => { + ref_pos += len_as_i64(op.len)?; + } + AlignmentOpKind::Insertion => { + let anchor = ref_pos.saturating_sub(1); + if anchor == anchor_pos { + return Some((AlignmentOpKind::Insertion, op.len)); + } + } + AlignmentOpKind::Deletion => { + let anchor = ref_pos.saturating_sub(1); + if anchor == anchor_pos { + return Some((AlignmentOpKind::Deletion, op.len)); + } + ref_pos += len_as_i64(op.len)?; + } + AlignmentOpKind::SoftClip | AlignmentOpKind::HardClip | AlignmentOpKind::Pad => {} + } + } + + None +} + +fn classify_expected_indel( + record: &AlignmentRecord, + locus: &GenomicLocus, + reference_len: usize, + alternate: &str, +) -> Result { + let alt_len = alternate.len(); + let anchor_start = locus.start.saturating_sub(1); + let anchor_end = locus.end; + + let covering = record.start <= locus.start && record.end >= locus.end; + if !covering { + return Ok(IndelClassification { + covering: false, + reference_like: false, + matches_alt: false, + observed_len: reference_len, + }); + } + + let mut observed_len = reference_len; + + for anchor in anchor_start..=anchor_end { + if let Some((kind, len)) = indel_at_anchor(record, anchor) { + observed_len = match kind { + AlignmentOpKind::Insertion => reference_len + len, + AlignmentOpKind::Deletion => reference_len.saturating_sub(len), + _ => reference_len, + }; + + return Ok(IndelClassification { + covering: true, + reference_like: false, + matches_alt: observed_len == alt_len, + observed_len, + }); + } + } + + Ok(IndelClassification { + covering: true, + reference_like: true, + matches_alt: false, + observed_len, + }) +} + fn describe_query(variant: &VariantSpec) -> &'static str { if variant.has_coordinates() { "variant_by_locus" @@ -974,7 +1003,10 @@ impl RowParser { } let trimmed = strip_bom(trimmed); - if let Some(prefix) = COMMENT_PREFIXES.iter().find(|prefix| trimmed.starts_with(**prefix)) { + if let Some(prefix) = COMMENT_PREFIXES + .iter() + .find(|prefix| trimmed.starts_with(**prefix)) + { let candidate = trimmed.trim_start_matches(prefix).trim(); if !candidate.is_empty() { let fields = self.parse_fields(candidate); @@ -1011,9 +1043,15 @@ impl RowParser { row_map.insert(normalize_name(&header[idx]), strip_inline_comment(&value)); } - let rsid = self.lookup(&row_map, "rsid").filter(|value| !value.is_empty()); - let chrom = self.lookup(&row_map, "chromosome").filter(|value| !value.is_empty()); - let position = self.lookup(&row_map, "position").and_then(|value| value.parse::().ok()); + let rsid = self + .lookup(&row_map, "rsid") + .filter(|value| !value.is_empty()); + let chrom = self + .lookup(&row_map, "chromosome") + .filter(|value| !value.is_empty()); + let position = self + .lookup(&row_map, "position") + .and_then(|value| value.parse::().ok()); if rsid.is_none() && (chrom.is_none() || position.is_none()) { return Ok(None); } @@ -1036,7 +1074,10 @@ impl RowParser { fn parse_fields(&self, line: &str) -> Vec { match self.delimiter { - Delimiter::Tab => line.split('\t').map(|field| field.trim().to_owned()).collect(), + Delimiter::Tab => line + .split('\t') + .map(|field| field.trim().to_owned()) + .collect(), Delimiter::Space => line.split_whitespace().map(str::to_owned).collect(), Delimiter::Comma => split_csv_line(line), } @@ -1054,7 +1095,9 @@ impl RowParser { let aliases = self.alias_map.get(key)?; for alias in aliases { let key = normalize_name(alias); - if let Some(value) = row_map.get(&key) && !value.is_empty() { + if let Some(value) = row_map.get(&key) + && !value.is_empty() + { return Some(value.clone()); } } @@ -1064,7 +1107,10 @@ impl RowParser { fn default_header(&self, field_count: usize) -> Vec { let base = ["rsid", "chromosome", "position", "genotype"]; if field_count <= base.len() { - base[..field_count].iter().map(|s| (*s).to_owned()).collect() + base[..field_count] + .iter() + .map(|s| (*s).to_owned()) + .collect() } else { let mut header: Vec = base.iter().map(|s| (*s).to_owned()).collect(); for idx in 0..(field_count - header.len()) { diff --git a/rust/bioscript-formats/src/lib.rs b/rust/bioscript-formats/src/lib.rs index f81eeaa..df274b3 100644 --- a/rust/bioscript-formats/src/lib.rs +++ b/rust/bioscript-formats/src/lib.rs @@ -7,6 +7,7 @@ clippy::unused_self )] +mod alignment; mod genotype; mod prepare; diff --git a/rust/bioscript-formats/src/prepare.rs b/rust/bioscript-formats/src/prepare.rs index 56cf31a..8defdd9 100644 --- a/rust/bioscript-formats/src/prepare.rs +++ b/rust/bioscript-formats/src/prepare.rs @@ -3,14 +3,12 @@ use std::{ path::{Path, PathBuf}, }; -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] use std::{ collections::hash_map::DefaultHasher, hash::{Hash, Hasher}, }; -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] -use rust_htslib::{bam, faidx}; +use noodles::{cram, fasta}; use crate::genotype::GenotypeSourceFormat; @@ -115,78 +113,59 @@ fn detect_alignment_input(path: &Path) -> bool { } fn ensure_alignment_index(path: &Path, cache_dir: &Path) -> Result { - #[cfg(any(target_os = "ios", target_os = "tvos"))] - { - let _ = (path, cache_dir); - return Err("alignment indexing is not supported on Apple mobile targets".to_owned()); - } - - #[cfg(not(any(target_os = "ios", target_os = "tvos")))] - { if let Some(existing) = adjacent_alignment_index(path) { return Ok(existing); } - let ext = if path + let is_cram = path .extension() .and_then(|ext| ext.to_str()) - .is_some_and(|ext| ext.eq_ignore_ascii_case("cram")) - { - "csi" - } else { - "bai" - }; + .is_some_and(|ext| ext.eq_ignore_ascii_case("cram")); + if !is_cram { + return Err(format!( + "alignment indexing only supports CRAM in the pure-Rust backend: {}", + path.display() + )); + } + + let ext = "crai"; let out = cache_dir.join(format!("{}.{ext}", stable_stem(path))); if out.exists() { return Ok(out); } - let idx_type = if ext == "bai" { - bam::index::Type::Bai - } else { - bam::index::Type::Csi(14) - }; - bam::index::build(path, Some(&out), idx_type, 1).map_err(|err| { + let index = cram::fs::index(path).map_err(|err| { format!( "failed to build alignment index {} for {}: {err}", out.display(), path.display() ) })?; + cram::crai::fs::write(&out, &index).map_err(|err| { + format!( + "failed to write alignment index {} for {}: {err}", + out.display(), + path.display() + ) + })?; Ok(out) - } } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] fn adjacent_alignment_index(path: &Path) -> Option { let lower = path.to_string_lossy().to_ascii_lowercase(); let candidates = if lower.ends_with(".cram") { vec![ path.with_extension("cram.crai"), path.with_extension("crai"), - path.with_extension("cram.csi"), - path.with_extension("csi"), ] } else { - vec![ - path.with_extension("bam.bai"), - path.with_extension("bai"), - path.with_extension("csi"), - ] + Vec::new() }; candidates.into_iter().find(|candidate| candidate.exists()) } fn ensure_reference_index(path: &Path, cache_dir: &Path) -> Result<(PathBuf, PathBuf), String> { - #[cfg(any(target_os = "ios", target_os = "tvos"))] - { - let _ = (path, cache_dir); - return Err("reference indexing is not supported on Apple mobile targets".to_owned()); - } - - #[cfg(not(any(target_os = "ios", target_os = "tvos")))] - { let adjacent = adjacent_reference_index(path); if let Some(index) = adjacent { return Ok((path.to_path_buf(), index)); @@ -200,26 +179,30 @@ fn ensure_reference_index(path: &Path, cache_dir: &Path) -> Result<(PathBuf, Pat let cached_index = adjacent_reference_index(&cached_reference) .unwrap_or_else(|| cached_reference_index_path(&cached_reference)); if !cached_index.exists() { - faidx::build(&cached_reference).map_err(|err| { + let index = fasta::fs::index(&cached_reference).map_err(|err| { format!( "failed to build FASTA index {} for {}: {err}", cached_index.display(), cached_reference.display() ) })?; + fasta::fai::fs::write(&cached_index, &index).map_err(|err| { + format!( + "failed to write FASTA index {} for {}: {err}", + cached_index.display(), + cached_reference.display() + ) + })?; } Ok((cached_reference, cached_index)) - } } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] fn adjacent_reference_index(path: &Path) -> Option { let candidate = cached_reference_index_path(path); candidate.exists().then_some(candidate) } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] fn cached_reference_index_path(path: &Path) -> PathBuf { if let Some(ext) = path.extension().and_then(|ext| ext.to_str()) { path.with_extension(format!("{ext}.fai")) @@ -228,7 +211,6 @@ fn cached_reference_index_path(path: &Path) -> PathBuf { } } -#[cfg(not(any(target_os = "ios", target_os = "tvos")))] fn create_reference_link(source: &Path, target: &Path) -> Result<(), String> { if let Some(parent) = target.parent() { fs::create_dir_all(parent) diff --git a/rust/bioscript-formats/tests/file_formats.rs b/rust/bioscript-formats/tests/file_formats.rs index af0b4e5..645a6b9 100644 --- a/rust/bioscript-formats/tests/file_formats.rs +++ b/rust/bioscript-formats/tests/file_formats.rs @@ -22,6 +22,15 @@ fn temp_dir(label: &str) -> PathBuf { dir } +fn repo_root() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("workspace rust dir") + .parent() + .expect("bioscript repo root") + .to_path_buf() +} + #[test] fn zip_genotype_file_is_auto_detected_and_readable() { let dir = temp_dir("zip-auto"); @@ -100,3 +109,257 @@ fn vcf_variant_lookup_reads_single_sample_calls() { .unwrap(); assert_eq!(observation.genotype.as_deref(), Some("DI")); } + +struct CramFixture { + cram: PathBuf, + reference: PathBuf, + reference_index: PathBuf, + input_index: PathBuf, +} + +fn cram_fixture_or_skip(test_name: &str) -> Option { + let root = repo_root(); + let fx = CramFixture { + cram: root.join("../test-data/1k-genomes/aligned/NA06985.final.cram"), + reference: root + .join("../test-data/1k-genomes/ref/GRCh38_full_analysis_set_plus_decoy_hla.fa"), + reference_index: root + .join("../test-data/1k-genomes/ref/GRCh38_full_analysis_set_plus_decoy_hla.fa.fai"), + input_index: root.join("../test-data/1k-genomes/aligned/NA06985.final.cram.crai"), + }; + for p in [ + &fx.cram, + &fx.reference, + &fx.reference_index, + &fx.input_index, + ] { + if !p.exists() { + eprintln!("skipping {test_name}: missing {}", p.display()); + return None; + } + } + Some(fx) +} + +fn open_cram_store(fx: &CramFixture) -> GenotypeStore { + GenotypeStore::from_file_with_options( + &fx.cram, + &GenotypeLoadOptions { + format: Some(GenotypeSourceFormat::Cram), + input_index: Some(fx.input_index.clone()), + reference_file: Some(fx.reference.clone()), + reference_index: Some(fx.reference_index.clone()), + }, + ) + .expect("open cram store") +} + +fn mini_fixtures_dir() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures") +} + +fn mini_cram_fixture() -> CramFixture { + let dir = mini_fixtures_dir(); + CramFixture { + cram: dir.join("mini.cram"), + reference: dir.join("mini.fa"), + reference_index: dir.join("mini.fa.fai"), + input_index: dir.join("mini.cram.crai"), + } +} + +fn mini_cram_fixture_with_bad_ref() -> CramFixture { + let dir = mini_fixtures_dir(); + CramFixture { + cram: dir.join("mini.cram"), + reference: dir.join("mini_bad_ref.fa"), + reference_index: dir.join("mini_bad_ref.fa.fai"), + input_index: dir.join("mini.cram.crai"), + } +} + +#[test] +fn cram_mini_fixture_streams_only_locus_overlapping_reads() { + // mini.cram has 2000 reads covering chr_test:500..2499. The streaming path + // should decode roughly until it passes the locus and stop — correctness is + // asserted via depth (exactly 50 reads overlap a single base in the middle). + // If the streaming + early-termination path breaks and falls back to full + // slice decode, the wall time still finishes fine on 2000 reads but this + // test also catches regressions that double-count or miss reads. + let fx = mini_cram_fixture(); + let store = open_cram_store(&fx); + + let start = std::time::Instant::now(); + let observation = store + .lookup_variant(&VariantSpec { + rsids: vec!["mini_locus_1000".to_owned()], + grch38: Some(bioscript_core::GenomicLocus { + chrom: "chr_test".to_owned(), + start: 1000, + end: 1000, + }), + reference: Some("A".to_owned()), + alternate: Some("C".to_owned()), + kind: Some(VariantKind::Snp), + ..VariantSpec::default() + }) + .expect("mini cram lookup"); + let elapsed = start.elapsed(); + + assert_eq!(observation.backend, "cram"); + assert_eq!( + observation.depth.unwrap_or(0), + 50, + "expected exactly 50 reads overlapping chr_test:1000, got {:?}", + observation.depth + ); + // All reads match reference in the fixture so alt_count should be zero. + assert_eq!(observation.ref_count.unwrap_or(0), 50); + assert_eq!(observation.alt_count.unwrap_or(0), 0); + assert!( + elapsed.as_millis() < 2000, + "mini CRAM lookup took {elapsed:?}, expected well under 2s" + ); +} + +#[test] +fn cram_mini_fixture_md5_mismatch_is_tolerated() { + // mini_bad_ref.fa has a single-base mutation at chr_test:2800, inside the + // slice span but far from our query locus at 1000. noodles' strict MD5 + // check will fail; bioscript must warn + retry unchecked + still return + // the correct genotype (the bases at pos 1000 are identical in both refs). + let fx = mini_cram_fixture_with_bad_ref(); + let store = open_cram_store(&fx); + + let observation = store + .lookup_variant(&VariantSpec { + rsids: vec!["mini_locus_1000".to_owned()], + grch38: Some(bioscript_core::GenomicLocus { + chrom: "chr_test".to_owned(), + start: 1000, + end: 1000, + }), + reference: Some("A".to_owned()), + alternate: Some("C".to_owned()), + kind: Some(VariantKind::Snp), + ..VariantSpec::default() + }) + .expect("mini cram lookup should succeed via md5 fallback"); + + assert_eq!(observation.backend, "cram"); + assert_eq!( + observation.depth.unwrap_or(0), + 50, + "expected exactly 50 reads after md5 fallback, got {:?}", + observation.depth + ); + // Bases at the query locus are the same in both references, so the + // fallback-decoded reads should still be ref-homozygous. + assert_eq!(observation.ref_count.unwrap_or(0), 50); + assert_eq!(observation.alt_count.unwrap_or(0), 0); +} + +#[test] +fn cram_apol1_snp_lookup_is_fast_and_correct() { + let Some(fx) = cram_fixture_or_skip("cram_apol1_snp_lookup_is_fast_and_correct") else { + return; + }; + let store = open_cram_store(&fx); + + let start = std::time::Instant::now(); + let observation = store + .lookup_variant(&VariantSpec { + rsids: vec!["rs73885319".to_owned()], + grch38: Some(bioscript_core::GenomicLocus { + chrom: "22".to_owned(), + start: 36_265_860, + end: 36_265_860, + }), + reference: Some("A".to_owned()), + alternate: Some("G".to_owned()), + kind: Some(VariantKind::Snp), + ..VariantSpec::default() + }) + .expect("apol1 lookup"); + let elapsed = start.elapsed(); + + assert_eq!(observation.backend, "cram"); + // NA06985 is reference-homozygous at APOL1 G1 site 1 per samtools mpileup. + let depth = observation.depth.unwrap_or(0); + assert!( + depth >= 10, + "expected >=10 reads at APOL1 locus, got {depth}" + ); + let ref_count = observation.ref_count.unwrap_or(0); + let alt_count = observation.alt_count.unwrap_or(0); + assert!( + ref_count > alt_count, + "NA06985 APOL1 G1 site 1 should be ref-dominant: ref={ref_count} alt={alt_count}" + ); + + // Slice-level CRAM decode is the hot path. Samtools does the same locus + // in ~40ms; we allow a generous ceiling to catch regressions (e.g. if the + // streaming/early-termination path breaks and we fall back to decoding + // every record in the slice, this blows past 10s). + assert!( + elapsed.as_secs() < 5, + "APOL1 CRAM lookup took {elapsed:?}, expected <5s (samtools does it in ~40ms)" + ); +} + +#[test] +fn cram_md5_mismatch_is_tolerated_and_returns_correct_result() { + // For NA06985.final.cram, the bundled GRCh38 FASTA's chr6 MD5 does not + // match the @SQ M5 the CRAM was encoded against (only chr22 matches). + // We must warn + fall back to unchecked decoding, and still return the + // correct genotype at the GLP1 rs10305420 locus. The correct call per + // samtools mpileup is reference-homozygous (CC). + let Some(fx) = + cram_fixture_or_skip("cram_md5_mismatch_is_tolerated_and_returns_correct_result") + else { + return; + }; + let store = open_cram_store(&fx); + + let observation = store + .lookup_variant(&VariantSpec { + rsids: vec!["rs10305420".to_owned()], + grch38: Some(bioscript_core::GenomicLocus { + chrom: "6".to_owned(), + start: 39_048_860, + end: 39_048_860, + }), + reference: Some("C".to_owned()), + alternate: Some("T".to_owned()), + kind: Some(VariantKind::Snp), + ..VariantSpec::default() + }) + .expect("glp1 lookup should succeed via md5 fallback"); + + assert_eq!(observation.backend, "cram"); + let depth = observation.depth.unwrap_or(0); + assert!( + depth >= 10, + "expected >=10 reads at GLP1 locus after md5 fallback, got {depth}" + ); + let genotype = observation + .genotype + .as_deref() + .expect("expected a genotype call"); + assert!( + genotype.chars().all(|c| c == 'C' || c == 'T'), + "unexpected genotype after md5 fallback: {genotype}" + ); + + // Parity with `samtools mpileup -f -r chr6:39048860-39048860`: + // that locus shows a mixed pileup (roughly half reference C, half T). + // We assert total depth matches samtools' reported depth within a small + // tolerance — confirms we are not silently dropping or duplicating reads + // after the unchecked-reference fallback. + let depth_i32 = i32::try_from(depth).unwrap_or(i32::MAX); + let samtools_depth: i32 = 41; + assert!( + (depth_i32 - samtools_depth).abs() <= 6, + "depth {depth_i32} differs from samtools mpileup depth {samtools_depth} by >6" + ); +} diff --git a/rust/bioscript-formats/tests/fixtures/generate_fixtures.sh b/rust/bioscript-formats/tests/fixtures/generate_fixtures.sh new file mode 100755 index 0000000..9526dbb --- /dev/null +++ b/rust/bioscript-formats/tests/fixtures/generate_fixtures.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Regenerate the tiny CRAM fixtures used by bioscript-formats integration tests. +# Run: ./generate_fixtures.sh (requires samtools) +# +# Produces: +# mini.fa, mini.fa.fai — 3 kb synthetic reference (contig chr_test) +# mini.cram, mini.cram.crai — 2000 reads covering pos 500..2500 +# mini_bad_ref.fa, .fai — reference with a single-base mutation at 2800 +# (inside the slice span → triggers MD5 mismatch) +# +# The fixtures are deterministic: regenerating produces byte-identical output +# (modulo samtools version differences in the CRAM container encoding). +set -euo pipefail +cd "$(dirname "$0")" + +python3 - <<'PY' +import random, pathlib +random.seed(20260414) +bases = "ACGT" +contig = "".join(random.choices(bases, k=3000)) +# Keep the reference all-uppercase so MD5 matches after noodles normalization. +with open("mini.fa", "w") as fh: + fh.write(">chr_test\n") + for i in range(0, len(contig), 60): + fh.write(contig[i:i+60] + "\n") + +# Tampered reference: flip one base at pos 2800 (0-indexed 2799). +tampered_list = list(contig) +idx = 2799 +original = tampered_list[idx] +tampered_list[idx] = next(b for b in "ACGT" if b != original) +tampered = "".join(tampered_list) +with open("mini_bad_ref.fa", "w") as fh: + fh.write(">chr_test\n") + for i in range(0, len(tampered), 60): + fh.write(tampered[i:i+60] + "\n") + +# 2000 reads, 50bp each, uniformly covering pos 500..2500 (1-based inclusive start). +# Sequences exactly match the reference (no mismatches) so depth parity is clean. +reads = [] +for i in range(2000): + pos = 500 + (i * (2000 // 2000)) % 2000 # spread across 500..2499 + pos = 500 + (i * 2000 // 2000) # i.e. 500..2499 in order + pos = 500 + i # 1-based; 500..2499 + seq = contig[pos-1:pos-1+50] + reads.append((f"r{i:05d}", pos, seq)) +# Stable sort by pos (already sorted since pos = 500+i). +sam = [] +sam.append("@HD\tVN:1.6\tSO:coordinate") +sam.append("@SQ\tSN:chr_test\tLN:3000") +for name, pos, seq in reads: + qual = "I" * 50 + sam.append(f"{name}\t0\tchr_test\t{pos}\t60\t50M\t*\t0\t0\t{seq}\t{qual}") +pathlib.Path("mini.sam").write_text("\n".join(sam) + "\n") +PY + +samtools faidx mini.fa +samtools faidx mini_bad_ref.fa + +samtools view -C --no-PG -T mini.fa -o mini.cram mini.sam +samtools index mini.cram + +rm -f mini.sam + +echo "fixtures written:" +ls -la mini.* mini_bad_ref.* diff --git a/rust/bioscript-formats/tests/fixtures/mini.cram b/rust/bioscript-formats/tests/fixtures/mini.cram new file mode 100644 index 0000000000000000000000000000000000000000..a5f2f28ec851a2b9ca3c9c99a1afcddeca4f36df GIT binary patch literal 790 zcmZ<`a`a_p%+1Wp)JrZ(%w<3T6-F4FiHV_M@88n@VnC6GO%40yyE!0I|6R}SRkAt;ye)qd)vclxSuAuZ zYWW$Cmes2)H!l<0RBrC%|0bYYr!ToohvkUuiFFgCXKF?!E$R+gt`)!B==j=G6Sj&U zjP5`8nK6Xl_|C(VqUU6$-xNq#(eQx%p5*JL#{7@+K7C1Fu~xdl=Knw2>uC#5K3tyZ zCY%|um(gM3j_Lb>ZoSrd=nRmGi-UQIHuN%$z9Su=U~Bx+!PtI*;b7YWHpYgH%?A!G zVPIrvSk|yih$YC6(J_EA#D$R|*jKvUd$M#xzmSs~GYcb7g)@j|W@HHR0n$c{3_yj9 zfX|OX;hz}?g;`En+i9tyT<`EWVZXQ9f8-%o&bJ~2|o=uNZ zi}~x~8fbjQYZC)2(98Ple?D@sa2IXcu)&Oh;Q~8QsDW)ke*hyJ1Dj6AJ_i;F78Vu( zpt~3t`|BAV=(1i~%D@1_8-T(v+{CQFSkxf!pg(|#nX|EqlZo+vW5vJ!%&ZH5-eO<@ z%Cd3-4fbaQhA7wq9)_16wr^x*;b8}c_ky-w1x5{q!%xdrv$6nt4nb8{n$;{CH literal 0 HcmV?d00001 diff --git a/rust/bioscript-formats/tests/fixtures/mini.cram.crai b/rust/bioscript-formats/tests/fixtures/mini.cram.crai new file mode 100644 index 0000000000000000000000000000000000000000..52da79029c32e62a20b32918e6c643bc66ca581e GIT binary patch literal 45 wcmb2|=3oE=VWS5orUnm8j0`qDF)=fHZen8o+{DP_DHDeQgN)dc8R9@y00>G8#{d8T literal 0 HcmV?d00001 diff --git a/rust/bioscript-formats/tests/fixtures/mini.fa b/rust/bioscript-formats/tests/fixtures/mini.fa new file mode 100644 index 0000000..09d7680 --- /dev/null +++ b/rust/bioscript-formats/tests/fixtures/mini.fa @@ -0,0 +1,51 @@ +>chr_test +TGTACCTCCGCCGATACGGTCCCGGGTGCATTACCATCTGTGCGCTTAATCTTGAATAGT +CTGTACCATGTTGTATGAGCTCTGAAACAAGAGCCCAGTTCGAATTAAACAGAGGTTCCA +ATGTACGCGAGCTGTAGATGCGTCGAGGTGGCGCACATACGCACTGTCTGATGTGAGCCT +GTATTTGACGCCGGCGGCAGGCTCTTATCTTATTCTTGGTGGTAGAGGTTGCCCTATCAA +TGTCTTAACTTCGTTAGATCTTCTTGTTCCCAACTTACTCCCTCCTGATTCGGCATAATC +CGCCTACTTTCCTGCGGAAAAAACCTAAGGATCGCGTACAGCATACGGCAGTATTGACAC +GCACCCATGATAGCCCAACTCCAGGGGACAGAGTACTACGCCAACTGTTGTAAAAGTTTA +GACAAGCCTCACGCGGATAGCTTAAACGTATCTGACTTCTGCAATGTCGGAAGGGGCGCA +TCCAGCTGTCGTAGGCACCTCCTTTACAATACTGAAGCACATTTGCAGAGCTTGCGCGAA +CGGTTGGAGATTGCTCACTCTCCAATCCCCCACAATGACTAACCTTCAAAGGGTCTCGTC +CAGGGTCAGAGTATAATCTAGGCTGCCTAGCATAACTTAGTCTAGTATGGCTCGCCCTCC +CGCGTGACTTCCCCCCCCGGGACGGCTTCGACGCAATCGCCCATGCAATCAAGACGGATG +ACGATTAGCTTATGATCTCCTATGCTTCGTAAAGAGAGCATGGCTTGGCTCGAGACGTCA +AGCGCTCGCTGTCTACTGTAACTATACACAAACAAGAGATCCTCCCTCGTGACAATAATC +CGGGTCCCATAGAGCACCATGGGCATACTTGGCGACACTTTCACCCCACAGGTCGAGATA +TGTATCGACATAATTCTCCACACGGGGAACTGGTGTCCCTAGTCAGTCCGCTGTGTATAT +TTGGCACTACGGCTCCCATCCCAGCTGGGTAAAGATGTTATCCCGATCAACAATCCTTTA +AGCTATTTTCATGTCTAGCGATACGCACATATGCCTCTGTTAACTGTCGCGACAACTCCT +GTACATGTGTGCCCAGTTATGTAAAAGCAGCTGCTAGCAAATATCATTGGCAACCCTTCC +CATATCCCTCTGGCATAAACCCGCTTCATCCAAACTAAGATAAGCGCACCTAGACCTGGA +GCGACGCCAGCGCTCGTGTAGTGCTCTTAAAACGGTTATAAACAGAATAGACACGGACTG +CCGTTGTTCCAGCCCTTATACAAACCGAAACTCGTCGTCACGTCGCCCCATCTTCATTGG +ACAGTACGATCCCCTTCTATCTGTGGGGGGTGCCAAGGCTTTCGTGTCCGCTCTGACGTG +GAATCACTCCATGTTCTCCGTGCGGGCGGAGAGGACAGCACTTATGAGCCTAATTAGGTT +GTATAGTCCGCCTCATCGGCATTGGCCGGTCGTCTTCGACCTCATAACGCATGCCAATAG +AGTGGGGACCACGCTACCTTCTAGTGGTGCGGGCTAGCTCGTACCGTTTAGCATACCGCG +ACCCCGTTACCGAGGATTAAGGGAATTTCGGGTACAAAAATCACTGATTGCGATAGGGTG +ATAGCCTTTGTAGAACCGGGTATTTCGAACCCGTGCGACTTCCCTGGGGAGCCGCCGACT +GAGACTGACGGCGCTATGTTGCAGCAGTCAATCCATTGCGCCGGATTGGCTACGACCTCC +CTATACTGCATACGGCGACACGACCGGAATGGAGATGACTCTTCTCAATGGAAGCTCACA +GGTGCTCTGTTGGGAACTCTTATCCGCCACGCTAATTCAGTCTGCGCTACTGCTCGCGCG +GTATTGAATTTGCCGGTCACAGGGCTATAACGGCATTCAAAAACAGAATCACCTAGTGTC +CAGCTTGCTAGGCGAAGTTCACACTTCCACCTGGAAGGCTACGTTTACTGGGATAATGCC +TGGGCAGCAAGTGGTAGGAGTCGATCTGGCCGTAATCATGGCGGACGACTTGCGGAGAAG +CCGTTGCTTACCCGTCGATGCGATAAATGTGAACTCACGGACTTGTTACTACAGGGACTC +CTGCCGAGATGCTACAAAGGCAATCATCACGGGGATGGCCCGCCACATGGATGCAACGCG +TCCCCCTCGATGAGAGTTCCATGACCGAGCGAACGTGCGCTCCGGGTCAGGGTCCCAAAC +CGATTATGTGGTCCCTGCGAGCCCCCCGTGACTTCACGATTCGTCTGTTCAATTATTGAT +GGCCATGAGTAATGCTGGCAATCTCCATCCGCTACGTAAAGACGGGCGTTAAGAGCACGA +GAACATACGATGCAGAGGGCATTATGGACTCCTTTTATTCAACGTGGCTGAGGTTCAAGC +CCGGCGACAGGGCTTGCCGCTCGTTTTCGCGGAGGTCCGGACGACTGTGGGACCGGCTGA +AGCAGAGATGTGGGTAGTGTAACCCCTGCTGTCTGTAGGTACCGCGTGTACGTCTTAAAA +TTGGGGCCGGCCAGGTCTAACCGCGACATGACGGCTTTGTGTACTGTGATCTTGAATGTT +GTTCTATTACCCCGCGCAGCACGCTTGCGCACGATGCCGTGGGGCTCCGCTAAGTTGTTC +GACTATTATCCTGCAACCACCTGTTCGTAAATTACCTCATCACAGTAATCACTTCGGCCG +CGCTAGGATCACCATAGATAACATAGCGGCGTCTGTAGGACGCGTCACATTTTCATCCCG +GTGTTAGCATTAGTAAGGCCCGGGACCCGTACTAGGGGTAAGTAAACGTGCGCCCCGCCT +CATTGAAGTCCGTTCTCGCCTTGATGTCGATTGCGTTCGAGTCGACATGGATTGAGGCCC +GAGCAGAGACTCCTTTGCCCATGAGTATGGGAGTCACGTTGAACAGGAGGACGATTATCT +AACCCATGAAAGACGGCTTTTCAAATTCCGAAATCTGTGTGGCTGTGTCGCGCGAGTGCG diff --git a/rust/bioscript-formats/tests/fixtures/mini.fa.fai b/rust/bioscript-formats/tests/fixtures/mini.fa.fai new file mode 100644 index 0000000..a531c14 --- /dev/null +++ b/rust/bioscript-formats/tests/fixtures/mini.fa.fai @@ -0,0 +1 @@ +chr_test 3000 10 60 61 diff --git a/rust/bioscript-formats/tests/fixtures/mini_bad_ref.fa b/rust/bioscript-formats/tests/fixtures/mini_bad_ref.fa new file mode 100644 index 0000000..55ef035 --- /dev/null +++ b/rust/bioscript-formats/tests/fixtures/mini_bad_ref.fa @@ -0,0 +1,51 @@ +>chr_test +TGTACCTCCGCCGATACGGTCCCGGGTGCATTACCATCTGTGCGCTTAATCTTGAATAGT +CTGTACCATGTTGTATGAGCTCTGAAACAAGAGCCCAGTTCGAATTAAACAGAGGTTCCA +ATGTACGCGAGCTGTAGATGCGTCGAGGTGGCGCACATACGCACTGTCTGATGTGAGCCT +GTATTTGACGCCGGCGGCAGGCTCTTATCTTATTCTTGGTGGTAGAGGTTGCCCTATCAA +TGTCTTAACTTCGTTAGATCTTCTTGTTCCCAACTTACTCCCTCCTGATTCGGCATAATC +CGCCTACTTTCCTGCGGAAAAAACCTAAGGATCGCGTACAGCATACGGCAGTATTGACAC +GCACCCATGATAGCCCAACTCCAGGGGACAGAGTACTACGCCAACTGTTGTAAAAGTTTA +GACAAGCCTCACGCGGATAGCTTAAACGTATCTGACTTCTGCAATGTCGGAAGGGGCGCA +TCCAGCTGTCGTAGGCACCTCCTTTACAATACTGAAGCACATTTGCAGAGCTTGCGCGAA +CGGTTGGAGATTGCTCACTCTCCAATCCCCCACAATGACTAACCTTCAAAGGGTCTCGTC +CAGGGTCAGAGTATAATCTAGGCTGCCTAGCATAACTTAGTCTAGTATGGCTCGCCCTCC +CGCGTGACTTCCCCCCCCGGGACGGCTTCGACGCAATCGCCCATGCAATCAAGACGGATG +ACGATTAGCTTATGATCTCCTATGCTTCGTAAAGAGAGCATGGCTTGGCTCGAGACGTCA +AGCGCTCGCTGTCTACTGTAACTATACACAAACAAGAGATCCTCCCTCGTGACAATAATC +CGGGTCCCATAGAGCACCATGGGCATACTTGGCGACACTTTCACCCCACAGGTCGAGATA +TGTATCGACATAATTCTCCACACGGGGAACTGGTGTCCCTAGTCAGTCCGCTGTGTATAT +TTGGCACTACGGCTCCCATCCCAGCTGGGTAAAGATGTTATCCCGATCAACAATCCTTTA +AGCTATTTTCATGTCTAGCGATACGCACATATGCCTCTGTTAACTGTCGCGACAACTCCT +GTACATGTGTGCCCAGTTATGTAAAAGCAGCTGCTAGCAAATATCATTGGCAACCCTTCC +CATATCCCTCTGGCATAAACCCGCTTCATCCAAACTAAGATAAGCGCACCTAGACCTGGA +GCGACGCCAGCGCTCGTGTAGTGCTCTTAAAACGGTTATAAACAGAATAGACACGGACTG +CCGTTGTTCCAGCCCTTATACAAACCGAAACTCGTCGTCACGTCGCCCCATCTTCATTGG +ACAGTACGATCCCCTTCTATCTGTGGGGGGTGCCAAGGCTTTCGTGTCCGCTCTGACGTG +GAATCACTCCATGTTCTCCGTGCGGGCGGAGAGGACAGCACTTATGAGCCTAATTAGGTT +GTATAGTCCGCCTCATCGGCATTGGCCGGTCGTCTTCGACCTCATAACGCATGCCAATAG +AGTGGGGACCACGCTACCTTCTAGTGGTGCGGGCTAGCTCGTACCGTTTAGCATACCGCG +ACCCCGTTACCGAGGATTAAGGGAATTTCGGGTACAAAAATCACTGATTGCGATAGGGTG +ATAGCCTTTGTAGAACCGGGTATTTCGAACCCGTGCGACTTCCCTGGGGAGCCGCCGACT +GAGACTGACGGCGCTATGTTGCAGCAGTCAATCCATTGCGCCGGATTGGCTACGACCTCC +CTATACTGCATACGGCGACACGACCGGAATGGAGATGACTCTTCTCAATGGAAGCTCACA +GGTGCTCTGTTGGGAACTCTTATCCGCCACGCTAATTCAGTCTGCGCTACTGCTCGCGCG +GTATTGAATTTGCCGGTCACAGGGCTATAACGGCATTCAAAAACAGAATCACCTAGTGTC +CAGCTTGCTAGGCGAAGTTCACACTTCCACCTGGAAGGCTACGTTTACTGGGATAATGCC +TGGGCAGCAAGTGGTAGGAGTCGATCTGGCCGTAATCATGGCGGACGACTTGCGGAGAAG +CCGTTGCTTACCCGTCGATGCGATAAATGTGAACTCACGGACTTGTTACTACAGGGACTC +CTGCCGAGATGCTACAAAGGCAATCATCACGGGGATGGCCCGCCACATGGATGCAACGCG +TCCCCCTCGATGAGAGTTCCATGACCGAGCGAACGTGCGCTCCGGGTCAGGGTCCCAAAC +CGATTATGTGGTCCCTGCGAGCCCCCCGTGACTTCACGATTCGTCTGTTCAATTATTGAT +GGCCATGAGTAATGCTGGCAATCTCCATCCGCTACGTAAAGACGGGCGTTAAGAGCACGA +GAACATACGATGCAGAGGGCATTATGGACTCCTTTTATTCAACGTGGCTGAGGTTCAAGC +CCGGCGACAGGGCTTGCCGCTCGTTTTCGCGGAGGTCCGGACGACTGTGGGACCGGCTGA +AGCAGAGATGTGGGTAGTGTAACCCCTGCTGTCTGTAGGTACCGCGTGTACGTCTTAAAA +TTGGGGCCGGCCAGGTCTAACCGCGACATGACGGCTTTGTGTACTGTGATCTTGAATGTT +GTTCTATTACCCCGCGCAGCACGCTTGCGCACGATGCCGTGGGGCTCCGCTAAGTTGTTC +GACTATTATCCTGCAACCACCTGTTCGTAAATTACCTCATCACAGTAATCACTTCGGCCG +CGCTAGGATCACCATAGATAACATAGCGGCGTCTGTAGGACGCGTCACATTTTCATCCCG +GTGTTAGCATTAGTAAGGCCCGGGACCCGTACTAGGGGTCAGTAAACGTGCGCCCCGCCT +CATTGAAGTCCGTTCTCGCCTTGATGTCGATTGCGTTCGAGTCGACATGGATTGAGGCCC +GAGCAGAGACTCCTTTGCCCATGAGTATGGGAGTCACGTTGAACAGGAGGACGATTATCT +AACCCATGAAAGACGGCTTTTCAAATTCCGAAATCTGTGTGGCTGTGTCGCGCGAGTGCG diff --git a/rust/bioscript-formats/tests/fixtures/mini_bad_ref.fa.fai b/rust/bioscript-formats/tests/fixtures/mini_bad_ref.fa.fai new file mode 100644 index 0000000..a531c14 --- /dev/null +++ b/rust/bioscript-formats/tests/fixtures/mini_bad_ref.fa.fai @@ -0,0 +1 @@ +chr_test 3000 10 60 61 diff --git a/rust/vendor/lexical-util/.cargo-ok b/rust/vendor/lexical-util/.cargo-ok new file mode 100644 index 0000000..5f8b795 --- /dev/null +++ b/rust/vendor/lexical-util/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/rust/vendor/lexical-util/.cargo_vcs_info.json b/rust/vendor/lexical-util/.cargo_vcs_info.json new file mode 100644 index 0000000..6de5f45 --- /dev/null +++ b/rust/vendor/lexical-util/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "ddfc0f6bfcc0085ff982f567f41f24a5541a14ea" + }, + "path_in_vcs": "lexical-util" +} \ No newline at end of file diff --git a/rust/vendor/lexical-util/CODE_OF_CONDUCT.md b/rust/vendor/lexical-util/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d209e5b --- /dev/null +++ b/rust/vendor/lexical-util/CODE_OF_CONDUCT.md @@ -0,0 +1,141 @@ +# Code of Conduct + +## When Something Happens + +If you see a Code of Conduct violation, follow these steps: + +1. Let the person know that what they did is not appropriate and ask them to stop and/or edit their message(s) or commits. +2. That person should immediately stop the behavior and correct the issue. +3. If this doesn’t happen, or if you're uncomfortable speaking up, [contact the maintainers](#contacting-maintainers). +4. As soon as available, a maintainer will look into the issue, and take [further action (see below)](#further-enforcement), starting with a warning, then temporary block, then long-term repo or organization ban. + +When reporting, please include any relevant details, links, screenshots, context, or other information that may be used to better understand and resolve the situation. + +**The maintainer team will prioritize the well-being and comfort of the recipients of the violation over the comfort of the violator.** See [some examples below](#enforcement-examples). + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers of this project pledge to making participation in our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, technical preferences, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +- Using welcoming and inclusive language. +- Being respectful of differing viewpoints and experiences. +- Gracefully accepting constructive feedback. +- Focusing on what is best for the community. +- Showing empathy and kindness towards other community members. +- Encouraging and raising up your peers in the project so you can all bask in hacks and glory. + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or advances, including when simulated online. The only exception to sexual topics is channels/spaces specifically for topics of sexual identity. +- Casual mention of slavery or indentured servitude and/or false comparisons of one's occupation or situation to slavery. Please consider using or asking about alternate terminology when referring to such metaphors in technology. +- Making light of/making mocking comments about trigger warnings and content warnings. +- Trolling, insulting/derogatory comments, and personal or political attacks. +- Public or private harassment, deliberate intimidation, or threats. +- Publishing others' private information, such as a physical or electronic address, without explicit permission. This includes any sort of "outing" of any aspect of someone's identity without their consent. +- Publishing private screenshots or quotes of interactions in the context of this project without all quoted users' *explicit* consent. +- Publishing of private communication that doesn't have to do with reporting harrassment. +- Any of the above even when [presented as "ironic" or "joking"](https://en.wikipedia.org/wiki/Hipster_racism). +- Any attempt to present "reverse-ism" versions of the above as violations. Examples of reverse-isms are "reverse racism", "reverse sexism", "heterophobia", and "cisphobia". +- Unsolicited explanations under the assumption that someone doesn't already know it. Ask before you teach! Don't assume what people's knowledge gaps are. +- [Feigning or exaggerating surprise](https://www.recurse.com/manual#no-feigned-surprise) when someone admits to not knowing something. +- "[Well-actuallies](https://www.recurse.com/manual#no-well-actuallys)" +- Other conduct which could reasonably be considered inappropriate in a professional or community setting. + +## Scope + +This Code of Conduct applies both within spaces involving this project and in other spaces involving community members. This includes the repository, its Pull Requests and Issue tracker, private email communications in the context of the project, and any events where members of the project are participating, as well as adjacent communities and venues affecting the project's members. + +Depending on the violation, the maintainers may decide that violations of this code of conduct that have happened outside of the scope of the community may deem an individual unwelcome, and take appropriate action to maintain the comfort and safety of its members. + +### Other Community Standards + +As a project on GitHub, this project is additionally covered by the [GitHub Community Guidelines](https://help.github.com/articles/github-community-guidelines/). + +Enforcement of those guidelines after violations overlapping with the above are the responsibility of the entities, and enforcement may happen in any or all of the services/communities. + +## Maintainer Enforcement Process + +Once the maintainers get involved, they will follow a documented series of steps and do their best to preserve the well-being of project members. This section covers actual concrete steps. + +### Contacting Maintainers + +You may get in touch with the maintainer team through any of the following methods: + + Through email: + ahuszagh@gmail.com (Alex Huszagh) + +### Further Enforcement + +If you've already followed the [initial enforcement steps](#maintainer-enforcement-process), these are the steps maintainers will take for further enforcement, as needed: + +1. Repeat the request to stop. +2. If the person doubles down, they will have offending messages removed or edited by a maintainers given an official warning. The PR or Issue may be locked. +3. If the behavior continues or is repeated later, the person will be blocked from participating for 24 hours. +4. If the behavior continues or is repeated after the temporary block, a long-term (6-12mo) ban will be used. + +On top of this, maintainers may remove any offending messages, images, contributions, etc, as they deem necessary. + +Maintainers reserve full rights to skip any of these steps, at their discretion, if the violation is considered to be a serious and/or immediate threat to the health and well-being of members of the community. These include any threats, serious physical or verbal attacks, and other such behavior that would be completely unacceptable in any social setting that puts our members at risk. + +Members expelled from events or venues with any sort of paid attendance will not be refunded. + +### Who Watches the Watchers? + +Maintainers and other leaders who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. These may include anything from removal from the maintainer team to a permanent ban from the community. + +Additionally, as a project hosted on both GitHub and npm, [their own Codes of Conducts may be applied against maintainers of this project](#other-community-standards), externally of this project's procedures. + +### Enforcement Examples + +#### The Best Case + +The vast majority of situations work out like this. This interaction is common, and generally positive. + +> Alex: "Yeah I used X and it was really crazy!" + +> Patt (not a maintainer): "Hey, could you not use that word? What about 'ridiculous' instead?" + +> Alex: "oh sorry, sure." -> edits old comment to say "it was really confusing!" + +#### The Maintainer Case + +Sometimes, though, you need to get maintainers involved. Maintainers will do their best to resolve conflicts, but people who were harmed by something **will take priority**. + +> Patt: "Honestly, sometimes I just really hate using $library and anyone who uses it probably sucks at their job." + +> Alex: "Whoa there, could you dial it back a bit? There's a CoC thing about attacking folks' tech use like that." + +> Patt: "I'm not attacking anyone, what's your problem?" + +> Alex: "@maintainers hey uh. Can someone look at this issue? Patt is getting a bit aggro. I tried to nudge them about it, but nope." + +> KeeperOfCommitBits: (on issue) "Hey Patt, maintainer here. Could you tone it down? This sort of attack is really not okay in this space." + +> Patt: "Leave me alone I haven't said anything bad wtf is wrong with you." + +> KeeperOfCommitBits: (deletes user's comment), "@patt I mean it. Please refer to the CoC over at (URL to this CoC) if you have questions, but you can consider this an actual warning. I'd appreciate it if you reworded your messages in this thread, since they made folks there uncomfortable. Let's try and be kind, yeah?" + +> Patt: "@keeperofbits Okay sorry. I'm just frustrated and I'm kinda burnt out and I guess I got carried away. I'll DM Alex a note apologizing and edit my messages. Sorry for the trouble." + +> KeeperOfCommitBits: "@patt Thanks for that. I hear you on the stress. Burnout sucks :/. Have a good one!" + +#### The Nope Case + +> PepeTheFrog🐸: "Hi, I am a literal actual nazi and I think white supremacists are quite fashionable." + +> Patt: "NOOOOPE. OH NOPE NOPE." + +> Alex: "JFC NO. NOPE. @keeperofbits NOPE NOPE LOOK HERE" + +> KeeperOfCommitBits: "👀 Nope. NOPE NOPE NOPE. 🔥" + +> PepeTheFrog🐸 has been banned from all organization or user repositories belonging to KeeperOfCommitBits. + +## Attribution + +This Code of Conduct was generated using [WeAllJS Code of Conduct Generator](https://npm.im/weallbehave), which is based on the [WeAllJS Code of Conduct](https://wealljs.org/code-of-conduct), which is itself based on +[Contributor Covenant](http://contributor-covenant.org), version 1.4, available at [http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4), and the LGBTQ in Technology Slack [Code of Conduct](http://lgbtq.technology/coc.html). diff --git a/rust/vendor/lexical-util/Cargo.lock b/rust/vendor/lexical-util/Cargo.lock new file mode 100644 index 0000000..6cdec59 --- /dev/null +++ b/rust/vendor/lexical-util/Cargo.lock @@ -0,0 +1,50 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "float16" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eb1bdc9979a09caeacb8528039f7a957f8209234e2c21395dd4abd601cd739" +dependencies = [ + "cfg-if", + "rustc_version", +] + +[[package]] +name = "lexical-util" +version = "1.0.7" +dependencies = [ + "float16", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" diff --git a/rust/vendor/lexical-util/Cargo.toml b/rust/vendor/lexical-util/Cargo.toml new file mode 100644 index 0000000..fd68876 --- /dev/null +++ b/rust/vendor/lexical-util/Cargo.toml @@ -0,0 +1,132 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.60.0" +name = "lexical-util" +version = "1.0.7" +authors = ["Alex Huszagh "] +build = false +exclude = [ + "assets/*", + "docs/*", + "etc/*", + "cargo-timing*.html", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Shared utilities for lexical creates." +readme = "README.md" +keywords = ["no_std"] +categories = [ + "value-formatting", + "encoding", + "no-std", +] +license = "MIT/Apache-2.0" +repository = "https://github.com/Alexhuszagh/rust-lexical" + +[package.metadata.docs.rs] +features = [ + "radix", + "format", + "write-integers", + "write-floats", + "parse-integers", + "parse-floats", + "f16", +] +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[features] +compact = [] +default = ["std"] +f128 = [ + "parse-floats", + "write-floats", +] +f16 = [ + "parse-floats", + "write-floats", + "float16", +] +format = [] +lint = [] +parse-floats = [] +parse-integers = [] +power-of-two = [] +radix = ["power-of-two"] +std = [] +write-floats = [] +write-integers = [] + +[lib] +name = "lexical_util" +path = "src/lib.rs" + +[[test]] +name = "algorithm_tests" +path = "tests/algorithm_tests.rs" + +[[test]] +name = "ascii_tests" +path = "tests/ascii_tests.rs" + +[[test]] +name = "bf16_tests" +path = "tests/bf16_tests.rs" + +[[test]] +name = "digit_tests" +path = "tests/digit_tests.rs" + +[[test]] +name = "f16_tests" +path = "tests/f16_tests.rs" + +[[test]] +name = "feature_format_tests" +path = "tests/feature_format_tests.rs" + +[[test]] +name = "format_builder_tests" +path = "tests/format_builder_tests.rs" + +[[test]] +name = "format_flags_tests" +path = "tests/format_flags_tests.rs" + +[[test]] +name = "iterator_tests" +path = "tests/iterator_tests.rs" + +[[test]] +name = "not_feature_format_tests" +path = "tests/not_feature_format_tests.rs" + +[[test]] +name = "num_tests" +path = "tests/num_tests.rs" + +[[test]] +name = "skip_tests" +path = "tests/skip_tests.rs" + +[dependencies.float16] +version = "0.1.0" +optional = true diff --git a/rust/vendor/lexical-util/Cargo.toml.orig b/rust/vendor/lexical-util/Cargo.toml.orig new file mode 100644 index 0000000..336664c --- /dev/null +++ b/rust/vendor/lexical-util/Cargo.toml.orig @@ -0,0 +1,69 @@ +[package] +authors = ["Alex Huszagh "] +autobenches = false +categories = ["value-formatting", "encoding", "no-std"] +description = "Shared utilities for lexical creates." +edition = "2021" +keywords = ["no_std"] +license = "MIT/Apache-2.0" +name = "lexical-util" +readme = "README.md" +repository = "https://github.com/Alexhuszagh/rust-lexical" +version = "1.0.7" +rust-version = "1.60.0" +exclude = [ + "assets/*", + "docs/*", + "etc/*", + "cargo-timing*.html" +] + +[dependencies] +float16 = { version = "0.1.0", optional = true } + +# FEATURES +# -------- +# In order to improve compile times, we have separate support +# for each numeric conversion. Since these features are additive, +# if more features are added, separate crates can add support +# for more features without requiring re-compilation of lexical. + +[features] +default = ["std"] +# Use the standard library. +std = [] +# Add support for numerical conversions with power-of-two strings. +power-of-two = [] +# Add support for numerical conversions with non-decimal strings. +radix = ["power-of-two"] +# Add support for parsing custom numerical formats. +format = [] +# Add support for writing integers. +write-integers = [] +# Add support for writing floats. +write-floats = [] +# Add support for parsing integers. +parse-integers = [] +# Add support for parsing floats. +parse-floats = [] +# Reduce code size at the cost of performance. +compact = [] +# Add support for the `f16` and `b16` half-point floating point numbers. +f16 = ["parse-floats", "write-floats", "float16"] + +# Internal only features. +# Enable the lint checks. +lint = [] + +# UNSUPPORTED +# ----------- +# Currently unsupported features. +# Enable support for 128-bit floats. Unsupported and unlikely to ever be. +# https://github.com/Alexhuszagh/rust-lexical/issues/46 +# Enable support for 16-bit floats. +# Enable support for 128-bit floats. +f128 = ["parse-floats", "write-floats"] + +[package.metadata.docs.rs] +features = ["radix", "format", "write-integers", "write-floats", "parse-integers", "parse-floats", "f16"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/rust/vendor/lexical-util/LICENSE-APACHE b/rust/vendor/lexical-util/LICENSE-APACHE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/rust/vendor/lexical-util/LICENSE-APACHE @@ -0,0 +1,201 @@ + 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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/rust/vendor/lexical-util/LICENSE-MIT b/rust/vendor/lexical-util/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/rust/vendor/lexical-util/LICENSE-MIT @@ -0,0 +1,23 @@ +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/rust/vendor/lexical-util/README.md b/rust/vendor/lexical-util/README.md new file mode 100644 index 0000000..14a2ba5 --- /dev/null +++ b/rust/vendor/lexical-util/README.md @@ -0,0 +1,414 @@ +# lexical + +High-performance numeric conversion routines for use in a `no_std` environment. This does not depend on any standard library features, nor a system allocator. Comprehensive benchmarks can be found at [lexical-benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks). + +**Similar Projects** + +If you want a minimal, performant float parser, recent versions of the Rust standard library should be [sufficient](https://github.com/rust-lang/rust/pull/86761). For high-performance integer formatters, look at [itoa](https://docs.rs/itoa/latest/itoa/). The [metrics](#metrics) section contains a detailed comparison of various crates and their performance in comparison to lexical. Lexical is the currently fastest Rust number formatter and parser, and is tested against: +- [itoa](https://crates.io/crates/itoa) +- [dtoa](https://crates.io/crates/dtoa) +- [ryu](https://crates.io/crates/ryu) +- Rust core library + +**Table of Contents** + +- [Getting Started](#getting-started) +- [Partial/Complete Parsers](#partialcomplete-parsers) +- [no_std](#no_std) +- [Features](#features) +- [Customization](#customization) + - [Number Format API](#number-format-api) + - [Options API](#options-api) +- [Documentation](#documentation) +- [Validation](#validation) +- [Metrics](#metrics) +- [Safety](#safety) +- [Platform Support](#platform-support) +- [Versioning and Version Support](#versioning-and-version-support) +- [Changelog](#changelog) +- [License](#license) +- [Contributing](#contributing) + +## Getting Started + +Add lexical to your `Cargo.toml`: + +```toml +[dependencies] +lexical-core = "^1.0" +``` + +And get started using lexical: + +```rust +// Number to string +use lexical_core::BUFFER_SIZE; +let mut buffer = [b'0'; BUFFER_SIZE]; +lexical_core::write(3.0, &mut buffer); // "3.0", always has a fraction suffix, +lexical_core::write(3, &mut buffer); // "3" + +// String to number. +let i: i32 = lexical_core::parse("3")?; // Ok(3), auto-type deduction. +let f: f32 = lexical_core::parse("3.5")?; // Ok(3.5) +let d: f64 = lexical_core::parse("3.5")?; // Ok(3.5), error checking parse. +let d: f64 = lexical_core::parse("3a")?; // Err(Error(_)), failed to parse. +``` + +In order to use lexical in generic code, the trait bounds `FromLexical` (for `parse`) and `ToLexical` (for `to_string`) are provided. + +```rust +/// Multiply a value in a string by multiplier, and serialize to string. +fn mul_2(value: &str, multiplier: T) + -> Result +where + T: lexical_core::ToLexical + lexical_core::FromLexical, +{ + let value: T = lexical_core::parse(value.as_bytes())?; + let mut buffer = [b'0'; lexical_core::BUFFER_SIZE]; + let bytes = lexical_core::write(value * multiplier, &mut buffer); + Ok(String::from_utf8(bytes).unwrap()) +} +``` + +## Partial/Complete Parsers + +Lexical has both partial and complete parsers: the complete parsers ensure the entire buffer is used while parsing, without ignoring trailing characters, while the partial parsers parse as many characters as possible, returning both the parsed value and the number of parsed digits. Upon encountering an error, lexical will return an error indicating both the error type and the index at which the error occurred inside the buffer. + +**Complete Parsers** + +```rust +// This will return Err(Error::InvalidDigit(3)), indicating +// the first invalid character occurred at the index 3 in the input +// string (the space character). +let x: i32 = lexical_core::parse(b"123 456")?; +``` + +**Partial Parsers** + +```rust +// This will return Ok((123, 3)), indicating that 3 digits were successfully +// parsed, and that the returned value is `123`. +let (x, count): (i32, usize) = lexical_core::parse_partial(b"123 456")?; +``` + +## no_std + +`lexical-core` does not depend on a standard library, nor a system allocator. To use `lexical-core` in a [`no_std`] environment, add the following to `Cargo.toml`: + +[`no_std`]: + +```toml +[dependencies.lexical-core] +version = "1.0.0" +default-features = false +# Can select only desired parsing/writing features. +features = ["write-integers", "write-floats", "parse-integers", "parse-floats"] +``` + +And get started using `lexical-core`: + +```rust +// A constant for the maximum number of bytes a formatter will write. +use lexical_core::BUFFER_SIZE; +let mut buffer = [b'0'; BUFFER_SIZE]; + +// Number to string. The underlying buffer must be a slice of bytes. +let count = lexical_core::write(3.0, &mut buffer); +assert_eq!(buffer[..count], b"3.0"); +let count = lexical_core::write(3i32, &mut buffer); +assert_eq!(buffer[..count], b"3"); + +// String to number. The input must be a slice of bytes. +let i: i32 = lexical_core::parse(b"3")?; // Ok(3), auto-type deduction. +let f: f32 = lexical_core::parse(b"3.5")?; // Ok(3.5) +let d: f64 = lexical_core::parse(b"3.5")?; // Ok(3.5), error checking parse. +let d: f64 = lexical_core::parse(b"3a")?; // Err(Error(_)), failed to parse. +``` + +## Features + +Lexical feature-gates each numeric conversion routine, resulting in faster compile times if certain numeric conversions. These features can be enabled/disabled for both `lexical-core` (which does not require a system allocator) and `lexical`. By default, all conversions are enabled. + +- **parse-floats**:   Enable string-to-float conversions. +- **parse-integers**:   Enable string-to-integer conversions. +- **write-floats**:   Enable float-to-string conversions. +- **write-integers**:   Enable integer-to-string conversions. + +Lexical is highly customizable, and contains numerous other optional features: + +- **std**:   Enable use of the Rust standard library (enabled by default). +- **power-of-two**:   Enable conversions to and from non-decimal strings. +
With power_of_two enabled, the radixes {2, 4, 8, 10, 16, and 32} are valid, otherwise, only 10 is valid. This enables common conversions to/from hexadecimal integers/floats, without requiring large pre-computed tables for other radixes.
+- **radix**:   Allow conversions to and from non-decimal strings. +
With radix enabled, any radix from 2 to 36 (inclusive) is valid, otherwise, only 10 is valid.
+- **format**:   Customize acceptable number formats for number parsing and writing. +
With format enabled, the number format is dictated through bitflags and masks packed into a u128. These dictate the valid syntax of parsed and written numbers, including enabling digit separators, requiring integer or fraction digits, and toggling case-sensitive exponent characters.
+- **compact**:   Optimize for binary size at the expense of performance. +
This minimizes the use of pre-computed tables, producing significantly smaller binaries.
+- **f16**:   Add support for numeric conversions to-and-from 16-bit floats. +
Adds f16, a half-precision IEEE-754 floating-point type, and bf16, the Brain Float 16 type, and numeric conversions to-and-from these floats. Note that since these are storage formats, and therefore do not have native arithmetic operations, all conversions are done using an intermediate f32.
+ +To ensure memory safety, we extensively fuzz the all numeric conversion routines. See the [Safety](#safety) section below for more information. + +Lexical also places a heavy focus on code bloat: with algorithms both optimized for performance and size. By default, this focuses on performance, however, using the `compact` feature, you can also opt-in to reduced code size at the cost of performance. The compact algorithms minimize the use of pre-computed tables and other optimizations at a major cost to performance. + +## Customization + +Lexical is extensively customizable to support parsing numbers from a wide variety of programming languages, such as `1_2_3`. However, lexical takes the concept of "you don't pay for what you don't use" seriously: enabling the `format` feature does not affect the performance of parsing regular numbers: only those with digit separators. + +> ⚠ **WARNING:** When changing the number of significant digits written, disabling the use of exponent notation, or changing exponent notation thresholds, `BUFFER_SIZE` may be insufficient to hold the resulting output. `WriteOptions::buffer_size_const` will provide a correct upper bound on the number of bytes written. If a buffer of insufficient length is provided, `lexical-core` will panic. + +Every language has competing specifications for valid numerical input, meaning a number parser for Rust will incorrectly accept or reject input for different programming or data languages. For example: + +```rust +// Valid in Rust strings. +// Not valid in JSON. +let f: f64 = lexical_core::parse(b"3.e7")?; // 3e7 + +// Let's only accept JSON floats. +const JSON: u128 = lexical_core::format::JSON; +const OPTIONS: ParseFloatOptions = ParseFloatOptions::new(); +let f: f64 = lexical_core::parse_with_options::<_, JSON>(b"3.0e7", &OPTIONS)?; // 3e7 +let f: f64 = lexical_core::parse_with_options::<_, JSON>(b"3.e7", &OPTIONS)?; // Errors! +``` + +Due the high variability in the syntax of numbers in different programming and data languages, we provide 2 different APIs to simplify converting numbers with different syntax requirements. + +- Number Format API (feature-gated via `format` or `power-of-two`). +
This is a packed struct contained flags to specify compile-time syntax rules for number parsing or writing. This includes features such as the radix of the numeric string, digit separators, case-sensitive exponent characters, optional base prefixes/suffixes, and more.
+- Options API. +
This contains run-time rules for parsing and writing numbers. This includes exponent break points, rounding modes, the exponent and decimal point characters, and the string representation of NaN and Infinity.
+ +A limited subset of functionality is documented in examples below, however, the complete specification can be found in the API reference documentation ([parse-float](https://docs.rs/lexical-parse-float/latest/lexical_parse_float/struct.Options.html), [parse-integer](https://docs.rs/lexical-parse-integer/latest/lexical_parse_integer/struct.Options.html), and [write-float](https://docs.rs/lexical-write-float/latest/lexical_write_float/struct.Options.html)). + +### Number Format API + +The number format class provides numerous flags to specify number syntax when parsing or writing. When the `power-of-two` feature is enabled, additional flags are added: + +- The radix for the significant digits (default `10`). +- The radix for the exponent base (default `10`). +- The radix for the exponent digits (default `10`). + +When the `format` feature is enabled, numerous other syntax and digit separator flags are enabled, including: + +- A digit separator character, to group digits for increased legibility. +- Whether leading, trailing, internal, and consecutive digit separators are allowed. +- Toggling required float components, such as digits before the decimal point. +- Toggling whether special floats are allowed or are case-sensitive. + +Many pre-defined constants therefore exist to simplify common use-cases, +including: +- [`JSON`], [`XML`], [`TOML`], [`YAML`], [`SQLite`], and many more. +- [`Rust`], [`Python`], [`C#`], [`FORTRAN`], [`COBOL`] literals and strings, and many more. + +[`JSON`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.JSON.html +[`XML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.XML.html +[`TOML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.TOML.html +[`YAML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.YAML.html +[`SQLite`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.SQLITE.html +[`Rust`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.RUST_LITERAL.html +[`Python`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.PYTHON_LITERAL.html +[`C#`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.CSHARP_LITERAL.html +[`FORTRAN`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.FORTRAN_LITERAL.html +[`COBOL`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.COBOL_LITERAL.html + +An example of building a custom number format is as follows: + +```rust +// this will panic if the format is invalid +const FORMAT: u128 = lexical_core::NumberFormatBuilder::new() + // Disable exponent notation. + .no_exponent_notation(true) + // Disable all special numbers, such as Nan and Inf. + .no_special(true) + .build_strict(); +``` + +### Options API + +The options API allows customizing number parsing and writing at run-time, such as specifying the maximum number of significant digits, exponent characters, and more. + +An example of building a custom options struct is as follows: + +```rust +use std::num; + +const OPTIONS: lexical_core::WriteFloatOptions = lexical_core::WriteFloatOptions::builder() + // Only write up to 5 significant digits, IE, `1.23456` becomes `1.2345`. + .max_significant_digits(num::NonZeroUsize::new(5)) + // Never write less than 5 significant digits, `1.1` becomes `1.1000`. + .min_significant_digits(num::NonZeroUsize::new(5)) + // Trim the trailing `.0` from integral float strings. + .trim_floats(true) + // Use a European-style decimal point. + .decimal_point(b',') + // Panic if we try to write NaN as a string. + .nan_string(None) + // Write infinity as "Infinity". + .inf_string(Some(b"Infinity")) + .build_strict(); +``` + +## Documentation + +Lexical's API reference can be found on [docs.rs](https://docs.rs/lexical), as can [lexical-core's](lexical-core). Detailed descriptions of the algorithms used can be found here: + +- [Parsing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Algorithm.md) +- [Parsing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Algorithm.md) +- [Writing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Algorithm.md) +- [Writing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Algorithm.md) + +In addition, descriptions of how lexical handles [digit separators](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/DigitSeparators.md) and implements [big-integer arithmetic](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/BigInteger.md) are also documented. + +## Validation + +**Float-Parsing** + +Float parsing is difficult to do correctly, and major bugs have been found in implementations from [libstdc++'s strtod](https://www.exploringbinary.com/glibc-strtod-incorrectly-converts-2-to-the-negative-1075/) to [Python](https://bugs.python.org/issue7632). In order to validate the accuracy of the lexical, we employ the following external tests: + +1. Hrvoje Abraham's [strtod](https://github.com/ahrvoje/numerics/tree/master/strtod) test cases. +2. Rust's [test-float-parse](https://github.com/rust-lang/rust/tree/64185f205dcbd8db255ad6674e43c63423f2369a/src/etc/test-float-parse) unittests. +3. Testbase's [stress tests](https://www.icir.org/vern/papers/testbase-report.pdf) for converting from decimal to binary. +4. Nigel Tao's [tests](https://github.com/nigeltao/parse-number-fxx-test-data) extracted from test suites for Freetype, Google's double-conversion library, IBM's IEEE-754R compliance test, as well as numerous other curated examples. +5. [Various](https://www.exploringbinary.com/glibc-strtod-incorrectly-converts-2-to-the-negative-1075/) [difficult](https://www.exploringbinary.com/how-glibc-strtod-works/) [cases](https://www.exploringbinary.com/how-strtod-works-and-sometimes-doesnt/) reported on blogs. + +Lexical is extensively used in production, the same float parsing algorithm has been adopted by Golang's and Rust's standard libraries, and is unlikely to have correctness issues. + +## Metrics + +Various benchmarks, binary sizes, and compile times are shown here. All the benchmarks can be found on [lexical-benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks?tab=readme-ov-file#latest-results). All benchmarks used a black box to avoid optimizing out the result and leading to misleading metrics. + +**Build Timings** + +The compile-times when building with all numeric conversions enabled. For a more fine-tuned breakdown, see [build timings](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BuildTimings.md). + +![Build Timings](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/timings_all_posix.svg) + +**Binary Size** + +The binary sizes of stripped binaries compiled at optimization level "2". For a more fine-tuned breakdown, see [binary sizes](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BinarySize.md). + +![Parse Stripped - Optimization Level "2"](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/size_parse_stripped_opt2_posix.svg) +![Write Stripped - Optimization Level "2"](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/size_write_stripped_opt2_posix.svg) + +### Benchmarks — Parse Integer + +**Random** + +A benchmark on randomly-generated integers uniformly distributed over the entire range. + +![Uniform Random Data](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/json_random%20-%20parse%20int%20-%20core,lexical.png) + +**Simple** + +A benchmark on randomly-generated integers from 1-1000. + +![Simple Random Data](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/json_simple%20-%20parse%20int%20-%20core,lexical.png) + +### Benchmarks — Parse Float + +**Real-World Datasets** + +A benchmark on parsing floats from various real-world data sets, including Canada, Mesh, and astronomical data (earth). + +![Canada](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/canada%20-%20parse%20float%20-%20core,lexical.png) + +![Earth](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/earth%20-%20parse%20float%20-%20core,lexical.png) + +![Mesh](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/mesh%20-%20parse%20float%20-%20core,lexical.png) + +**Random** + +A benchmark on randomly-generated integers uniformly distributed over the entire range. + +![Random Big Integer](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/random_big_ints%20-%20parse%20float%20-%20core,lexical.png) + +**Simple** + +A benchmark on randomly-generated integers from 1-1000. + +![Random Simple](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/random_simple_int64%20-%20parse%20float%20-%20core,lexical.png) + +### Benchmarks — Write Integer + +**Random** + +A benchmark on randomly-generated integers uniformly distributed over the entire range. + +![Random Uniform](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/json_chain_random%20-%20write%20int%20-%20fmt,itoa,lexical.png) + +**Simple** + +![Random Simple](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/json_simple%20-%20write%20int%20-%20fmt,itoa,lexical.png) + +**Large** + +![Random Large](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/random_large%20-%20write%20int%20-%20fmt,itoa,lexical.png) + +### Benchmarks — Write Float + +**Big Integer** + +A benchmarks for values with a large integers. + +![Big Integers](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/random_big_ints%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png) + +**Simple 64-Bit Inteers** + +![Simple Int64](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/random_simple_int64%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png) + +**Random** + +![Random](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/latest/plot/json%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png) + +## Safety + +Due to the use of memory unsafe code in the library, we extensively fuzz our float writers and parsers. The fuzz harnesses may be found under [fuzz](https://github.com/Alexhuszagh/rust-lexical/tree/main/fuzz), and are run continuously. So far, we've parsed and written over 72 billion floats. + +## Platform Support + +lexical-core is tested on a wide variety of platforms, including big and small-endian systems, to ensure portable code. Supported architectures include: +- x86_64 Linux, Windows, macOS, Android, iOS, FreeBSD, and NetBSD. +- x86 Linux, macOS, Android, iOS, and FreeBSD. +- aarch64 (ARM8v8-A) Linux, Android, and iOS. +- armv7 (ARMv7-A) Linux, Android, and iOS. +- arm (ARMv6) Linux, and Android. +- powerpc (PowerPC) Linux. +- powerpc64 (PPC64) Linux. +- powerpc64le (PPC64LE) Linux. +- s390x (IBM Z) Linux. + +lexical-core should also work on a wide variety of other architectures and ISAs. If you have any issue compiling lexical-core on any architecture, please file a bug report. + +## Versioning and Version Support + +**Version Support** + +The currently supported versions are: +- v1.0.x + +Due to security considerations, all other versions are not supported and security advisories exist for them. + +**Rustc Compatibility** + +- v1.0.x supports 1.63+, including stable, beta, and nightly. + +Please report any errors compiling a supported `lexical` version on a compatible Rustc version. + +**Versioning** + +`lexical` uses [semantic versioning](https://semver.org/). Removing support for Rustc versions newer than the latest stable Debian or Ubuntu version is considered an incompatible API change, requiring a major version change. + +## Changelog + +All changes are documented in [CHANGELOG](https://github.com/Alexhuszagh/rust-lexical/blob/main/CHANGELOG). + +## License + +Lexical is dual licensed under the Apache 2.0 license as well as the MIT license. See the [LICENSE.md](LICENSE.md) file for full license details. + +## Contributing + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in `lexical` by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. Contributing to the repository means abiding by the [code of conduct](https://github.com/Alexhuszagh/rust-lexical/blob/main/CODE_OF_CONDUCT.md). + +For the process on how to contribute to `lexical`, see the [development](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/Development.md) quick-start guide. diff --git a/rust/vendor/lexical-util/src/algorithm.rs b/rust/vendor/lexical-util/src/algorithm.rs new file mode 100644 index 0000000..c01c7af --- /dev/null +++ b/rust/vendor/lexical-util/src/algorithm.rs @@ -0,0 +1,41 @@ +//! Simple, shared algorithms for slices and iterators. + +use crate::num::Integer; + +/// Copy bytes from source to destination. +/// +/// This is only used in our compact and radix integer formatted, so +/// performance isn't the highest consideration here. +#[inline(always)] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +pub fn copy_to_dst>(dst: &mut [T], src: Bytes) -> usize { + let src = src.as_ref(); + dst[..src.len()].copy_from_slice(src); + + src.len() +} + +/// Count the number of trailing characters equal to a given value. +#[inline(always)] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +pub fn rtrim_char_count(slc: &[u8], c: u8) -> usize { + slc.iter().rev().take_while(|&&si| si == c).count() +} + +/// Count the number of leading characters equal to a given value. +#[inline(always)] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +pub fn ltrim_char_count(slc: &[u8], c: u8) -> usize { + slc.iter().take_while(|&&si| si == c).count() +} + +/// Check to see if parsing the float cannot possible overflow. +/// +/// This allows major optimizations for those types, since we can skip checked +/// arithmetic. Adapted from the rust [corelib][`core`]. +/// +/// [`core`]: +#[inline(always)] +pub fn cannot_overflow(length: usize, radix: u32) -> bool { + length <= T::overflow_digits(radix) +} diff --git a/rust/vendor/lexical-util/src/api.rs b/rust/vendor/lexical-util/src/api.rs new file mode 100644 index 0000000..75cb32d --- /dev/null +++ b/rust/vendor/lexical-util/src/api.rs @@ -0,0 +1,442 @@ +//! Implement string conversion routines in a single trait. + +// NOTE: +// We use macros to define the traits, rather than implement here +// since we can't define traits for types when both are defined outside +// the current crate, including in workspaces. + +// FROM LEXICAL + +/// Define the [`FromLexical`] trait. +/// +/// * `name`: The name of the crate calling the function. +/// * `value`: A numerical value to use for the example. +/// * `t`: The type of the number for the example. +/// * `len`: The length of the string form of `value`. +/// +/// # Examples +/// +/// ```rust,ignore +/// from_lexical!("lexical_core", 1234, u64, 4); +/// ``` +/// +/// [`FromLexical`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FromLexical.html +#[macro_export] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] +macro_rules! from_lexical { + () => { + pub trait FromLexical: lexical_util::num::Number { + fn from_lexical(bytes: &[u8]) -> lexical_util::result::Result; + + fn from_lexical_partial(bytes: &[u8]) -> lexical_util::result::Result<(Self, usize)>; + } + }; + + ($name:literal, $value:literal, $t:ty, $len:literal $(, #[$attr:meta])? $(,)?) => { + /// Trait for numerical types that can be parsed from bytes. + $(#[$attr])? + pub trait FromLexical: lexical_util::num::Number { + /// Checked parser for a string-to-number conversion. + /// + /// This method parses the entire string, returning an error if + /// any invalid digits are found during parsing. Returns a [`Result`] + /// containing either the parsed value, or an error containing + /// any errors that occurred during parsing. + /// + /// * `bytes` - Slice containing a numeric string. + /// + /// # Examples + /// + /// ```rust + #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")] + #[doc = concat!("use ", $name, "::FromLexical;")] + /// + #[doc = concat!("let value = \"", stringify!($value), "\";")] + #[doc = concat!("let parsed = ", stringify!($t), "::from_lexical(value.as_bytes());")] + #[doc = concat!("assert_eq!(parsed, Ok(", stringify!($value), "));")] + /// ``` + fn from_lexical(bytes: &[u8]) -> lexical_util::result::Result; + + /// Checked parser for a string-to-number conversion. + /// + /// This method parses until an invalid digit is found (or the end + /// of the string), returning the number of processed digits + /// and the parsed value until that point. Returns a [`Result`] + /// containing either the parsed value and the number of processed + /// digits, or an error containing any errors that occurred during + /// parsing. + /// + /// * `bytes` - Slice containing a numeric string. + /// + /// # Examples + /// + /// ```rust + #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")] + #[doc = concat!("use ", $name, "::FromLexical;")] + /// + #[doc = concat!("let value = \"", stringify!($value), "\";")] + #[doc = concat!("let parsed = ", stringify!($t), "::from_lexical_partial(value.as_bytes());")] + #[doc = concat!("assert_eq!(parsed, Ok((", stringify!($value), ", ", stringify!($len), ")));")] + /// ``` + fn from_lexical_partial(bytes: &[u8]) -> lexical_util::result::Result<(Self, usize)>; + } + }; +} + +/// Define the [`FromLexicalWithOptions`] trait. +/// +/// * `name`: The name of the crate calling the function. +/// * `value`: A numerical value to use for the example. +/// * `t`: The type of the number for the example. +/// * `len`: The length of the string form of `value`. +/// * `ops_t`: The options type. +/// +/// # Examples +/// +/// ```rust,ignore +/// from_lexical_with_options!("lexical_core", 1234, u64, 4, ParseIntegerOptions); +/// ``` +/// +/// [`FromLexicalWithOptions`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FromLexicalWithOptions.html +#[macro_export] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] +macro_rules! from_lexical_with_options { + () => { + pub trait FromLexicalWithOptions: lexical_util::num::Number { + type Options: lexical_util::options::ParseOptions; + + fn from_lexical_with_options( + bytes: &[u8], + options: &Self::Options, + ) -> lexical_util::result::Result; + + fn from_lexical_partial_with_options( + bytes: &[u8], + options: &Self::Options, + ) -> lexical_util::result::Result<(Self, usize)>; + } + }; + + ($name:literal, $value:literal, $t:ty, $len:literal, $ops_t:ty $(, #[$attr:meta])? $(,)?) => { + /// Trait for numerical types that can be parsed from bytes with custom options. + /// + /// The [`Options`][Self::Options] type specifies the configurable + /// options to provide. + $(#[$attr])? + pub trait FromLexicalWithOptions: lexical_util::num::Number { + /// Custom formatting options for parsing a number. + type Options: lexical_util::options::ParseOptions; + + /// Checked parser for a string-to-number conversion. + /// + /// This method parses the entire string, returning an error if + /// any invalid digits are found during parsing. The parsing + /// is dictated by the options, which specifies special + /// float strings, required float components, digit separators, + /// exponent characters, and more. Returns a [`Result`] containing + /// either the parsed value, or an error containing any errors + /// that occurred during parsing. + /// + /// * `FORMAT` - Flags and characters designating the number grammar. + /// * `bytes` - Slice containing a numeric string. + /// * `options` - Options to dictate number parsing. + /// + /// The `FORMAT` packed struct is built using [`NumberFormatBuilder`]. + /// Any invalid number format will prevent parsing, returning + /// the appropriate format error. If you are unsure which format + /// to use, use [`STANDARD`]. + /// + /// # Examples + /// + /// ```rust + #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")] + #[doc = concat!("use ", $name, "::{format, FromLexicalWithOptions, ", stringify!($ops_t), "};")] + /// + /// const FORMAT: u128 = format::STANDARD; + #[doc = concat!("const OPTIONS: ", stringify!($ops_t), " = ", stringify!($ops_t), "::new();")] + #[doc = concat!("let value = \"", stringify!($value), "\";")] + #[doc = concat!("let parsed = ", stringify!($t), "::from_lexical_with_options::(value.as_bytes(), &OPTIONS);")] + #[doc = concat!("assert_eq!(parsed, Ok(", stringify!($value), "));")] + /// ``` + /// + /// [`NumberFormatBuilder`]: lexical_util::format::NumberFormatBuilder + /// [`STANDARD`]: lexical_util::format::STANDARD + fn from_lexical_with_options( + bytes: &[u8], + options: &Self::Options, + ) -> lexical_util::result::Result; + + /// Checked parser for a string-to-number conversion. + /// + /// This method parses until an invalid digit is found (or the end + /// of the string), returning the number of processed digits + /// and the parsed value until that point. Returns a [`Result`] + /// containing either the parsed value and the number of + /// processed digits, or an error containing any errors that + /// occurred during parsing. + /// + /// * `FORMAT` - Flags and characters designating the number grammar. + /// * `bytes` - Slice containing a numeric string. + /// * `options` - Options to dictate number parsing. + /// + /// The `FORMAT` packed struct is built using [`NumberFormatBuilder`]. + /// Any invalid number format will prevent parsing, returning + /// the appropriate format error. If you are unsure which format + /// to use, use [`STANDARD`]. + /// + /// # Examples + /// + /// ```rust + #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")] + #[doc = concat!("use ", $name, "::{format, FromLexicalWithOptions, ", stringify!($ops_t), "};")] + /// + /// const FORMAT: u128 = format::STANDARD; + #[doc = concat!("const OPTIONS: ", stringify!($ops_t), " = ", stringify!($ops_t), "::new();")] + /// + #[doc = concat!("let value = \"", stringify!($value), "\";")] + #[doc = concat!( + "let parsed = ", + stringify!($t), + "::from_lexical_partial_with_options::(value.as_bytes(), &OPTIONS);" + )] + #[doc = concat!("assert_eq!(parsed, Ok((", stringify!($value), ", ", stringify!($len), ")));")] + /// ``` + /// + /// [`NumberFormatBuilder`]: lexical_util::format::NumberFormatBuilder + /// [`STANDARD`]: lexical_util::format::STANDARD + fn from_lexical_partial_with_options( + bytes: &[u8], + options: &Self::Options, + ) -> lexical_util::result::Result<(Self, usize)>; + } + }; +} + +// TO LEXICAL + +/// Define the [`ToLexical`] trait. +/// +/// * `name`: The name of the crate calling the function. +/// * `value`: A numerical value to use for the example. +/// * `t`: The type of the number for the example. +/// +/// # Examples +/// +/// ```rust,ignore +/// to_lexical!("lexical_core", 1234, u64); +/// ``` +/// +/// [`ToLexical`]: https://docs.rs/lexical-core/latest/lexical_core/trait.ToLexical.html +#[macro_export] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +macro_rules! to_lexical { + () => { + pub trait ToLexical: + lexical_util::constants::FormattedSize + lexical_util::num::Number + { + fn to_lexical<'a>(self, bytes: &'a mut [u8]) -> &'a mut [u8]; + } + }; + + ($name:literal, $value:literal, $t:ty $(, #[$attr:meta])? $(,)?) => { + /// Trait for numerical types that can be serialized to bytes. + /// + /// To determine the number of bytes required to serialize a value to + /// string, check the associated constants from a required trait: + /// - [`FORMATTED_SIZE`]: The number of bytes required for any number for any + /// radix, that is, `2` to `36`. + /// - [`FORMATTED_SIZE_DECIMAL`]: The number of bytes required for decimal (base + /// 10) numbers. + /// + /// [`FORMATTED_SIZE`]: crate::FormattedSize::FORMATTED_SIZE + /// [`FORMATTED_SIZE_DECIMAL`]: crate::FormattedSize::FORMATTED_SIZE_DECIMAL + $(#[$attr])? + pub trait ToLexical: + lexical_util::constants::FormattedSize + lexical_util::num::Number + { + /// Serializer for a number-to-string conversion. + /// + /// Returns a subslice of the input buffer containing the written bytes, + /// starting from the same address in memory as the input slice. That + /// is, the `bytes` provided to the function and the returned buffer + /// reference the same buffer, just with the number of elements truncated + /// to the written digits. + /// + /// * `value` - Number to serialize. + /// * `bytes` - Buffer to write number to. + /// + /// # Examples + /// + /// ```rust + /// use core::str; + /// + #[doc = concat!("use ", $name, "::{format, FormattedSize, ToLexical};")] + /// + #[doc = concat!("let value: ", stringify!($t), " = ", stringify!($value), ";")] + #[doc = concat!("let mut buffer = [0u8; ", stringify!($t), "::FORMATTED_SIZE_DECIMAL];")] + /// let digits = value.to_lexical(&mut buffer); + #[doc = concat!("assert_eq!(str::from_utf8(digits), Ok(\"", stringify!($value), "\"));")] + /// ``` + /// + /// # Panics + /// + /// Panics if the buffer is not of sufficient size. The caller + /// must provide a slice of sufficient size. In order to ensure + /// the function will not panic, ensure the buffer has at least + /// [`FORMATTED_SIZE_DECIMAL`] elements. + /// + /// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL + fn to_lexical<'a>(self, bytes: &'a mut [u8]) -> &'a mut [u8]; + } + }; +} + +/// Define the [`ToLexicalWithOptions`] trait. +/// +/// * `name`: The name of the crate calling the function. +/// * `value`: A numerical value to use for the example. +/// * `t`: The type of the number for the example. +/// * `ops_t`: The options type. +/// +/// # Examples +/// +/// ```rust,ignore +/// to_lexical_with_options!("lexical_core", 1234, u64, WriteIntegerOptions); +/// ``` +/// +/// [`ToLexicalWithOptions`]: https://docs.rs/lexical-core/latest/lexical_core/trait.ToLexicalWithOptions.html +#[macro_export] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +macro_rules! to_lexical_with_options { + () => { + pub trait ToLexicalWithOptions: + lexical_util::constants::FormattedSize + lexical_util::num::Number + { + type Options: lexical_util::options::WriteOptions; + + fn to_lexical_with_options<'a, const FORMAT: u128>( + self, + bytes: &'a mut [u8], + options: &Self::Options, + ) -> &'a mut [u8]; + } + }; + + ($name:literal, $value:literal, $t:ty, $ops_t:ty $(, #[$attr:meta])? $(,)?) => { + /// Trait for numerical types that can be serialized to bytes with custom + /// options. + /// + /// To determine the number of bytes required to serialize a value to + /// string, check the associated constants from a required trait: + /// - [`FORMATTED_SIZE`]: The number of bytes required for any number for any + /// radix, that is, `2` to `36`. + /// - [`FORMATTED_SIZE_DECIMAL`]: The number of bytes required for decimal (base + /// 10) numbers. + /// + /// The [`Options`][Self::Options] type specifies the configurable options to provide. + /// + /// [`FORMATTED_SIZE`]: crate::FormattedSize::FORMATTED_SIZE + /// [`FORMATTED_SIZE_DECIMAL`]: crate::FormattedSize::FORMATTED_SIZE_DECIMAL + $(#[$attr])? + pub trait ToLexicalWithOptions: + lexical_util::constants::FormattedSize + lexical_util::num::Number + { + /// Custom formatting options for writing a number. + type Options: lexical_util::options::WriteOptions; + + /// Serializer for a number-to-string conversion. + /// + /// Returns a subslice of the input buffer containing the written bytes, + /// starting from the same address in memory as the input slice. That + /// is, the `bytes` provided to the function and the returned buffer + /// reference the same buffer, just with the number of elements truncated + /// to the written digits. + /// + /// * `FORMAT` - Flags and characters designating the number grammar. + /// * `value` - Number to serialize. + /// * `bytes` - Buffer to write number to. + /// * `options` - Options for number formatting. + /// + /// `FORMAT` should be built using [`NumberFormatBuilder`] and includes + /// options such as the numerical radix for writing the value to string. + /// `options` specificies extra, additional configurations such as + /// special values like `NaN` or `+Infinity` for how to serialize + /// the number. + /// + /// [`NumberFormatBuilder`]: crate::NumberFormatBuilder + /// + /// # Examples + /// + /// ```rust + /// use core::str; + /// + #[doc = concat!( + "use ", + $name, + "::{format, FormattedSize, ", + stringify!($ops_t), + ", ToLexicalWithOptions};" + )] + /// + /// const FORMAT: u128 = format::STANDARD; + #[doc = concat!("const OPTIONS: ", stringify!($ops_t), " = ", stringify!($ops_t), "::new();")] + #[doc = concat!( + "const BUFFER_SIZE: usize = OPTIONS.buffer_size_const::<", + stringify!($t), + ", FORMAT>();" + )] + /// + #[doc = concat!("let value: ", stringify!($t), " = ", stringify!($value), ";")] + /// let mut buffer = [0u8; BUFFER_SIZE]; + /// let digits = value.to_lexical_with_options::(&mut buffer, &OPTIONS); + #[doc = concat!("assert_eq!(str::from_utf8(digits), Ok(\"", stringify!($value), "\"));")] + /// ``` + /// + /// # Panics + /// + /// Panics if the buffer is not of sufficient size. The caller + /// must provide a slice of sufficient size. In order to ensure + /// the function will not panic, ensure the buffer has at least + /// [`Options::buffer_size_const`] elements. This is required + /// only when changing the number of significant digits, the + /// exponent break point, or disabling scientific notation. + /// + /// If you are not using [`min_significant_digits`] (floats only), + /// 1200 bytes is always enough to hold the the output for a custom + /// radix, and `400` is always enough for decimal strings. + /// + /// **Floats Only** + /// + /// These panics are only when using uncommon features for float + /// writing, represent configuration errors, so runtime error + /// handling is not provided. + /// + /// Also panics if the provided number format is invalid, or + /// if the mantissa radix is not equal to the exponent base + /// and the mantissa radix/exponent base combinations are + /// not in the following list: + /// + /// - `4, 2` + /// - `8, 2` + /// - `16, 2` + /// - `32, 2` + /// - `16, 4` + /// + /// Panics as well if `the` NaN or `Inf` string provided to the writer + /// is disabled, but the value provided is `NaN` or `Inf`, respectively. + /// + #[doc = concat!( + "[`Options::buffer_size_const`]: crate::", + stringify!($ops_t), + "::buffer_size_const" + )] + /// [`FORMATTED_SIZE`]: crate::FormattedSize::FORMATTED_SIZE + /// [`min_significant_digits`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.min_significant_digits + fn to_lexical_with_options<'a, const FORMAT: u128>( + self, + bytes: &'a mut [u8], + options: &Self::Options, + ) -> &'a mut [u8]; + } + }; +} diff --git a/rust/vendor/lexical-util/src/ascii.rs b/rust/vendor/lexical-util/src/ascii.rs new file mode 100644 index 0000000..ec2f393 --- /dev/null +++ b/rust/vendor/lexical-util/src/ascii.rs @@ -0,0 +1,47 @@ +//! Utilities for working with ASCII characters. + +/// Determine if a character is a valid ASCII character for float grammar. +#[inline(always)] +pub const fn is_valid_ascii(c: u8) -> bool { + // Below 0x20 is mostly control characters, with no representation. + // 0x7F is a control character, DEL, so don't include it. + // We also want the few visual characters below 0x20: + // 0x09 - Horizontal Tab + // 0x0A - Newline + // 0x0B - Vertical Tab (Deprecated) + // 0x0C - Form Feed (Deprecated) + // 0x0D - Carriage Return + (c >= 0x09 && c <= 0x0d) || (c >= 0x20 && c < 0x7F) +} + +/// Determine if a slice is all valid ASCII characters for float grammar. +#[inline(always)] +pub const fn is_valid_ascii_slice(slc: &[u8]) -> bool { + let mut index = 0; + while index < slc.len() { + if !is_valid_ascii(slc[index]) { + return false; + } + index += 1; + } + true +} + +/// Determine if a character is a valid ASCII letter. +#[inline(always)] +pub const fn is_valid_letter(c: u8) -> bool { + (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) +} + +/// Determine if a slice is all valid ASCII letters. +#[inline(always)] +pub const fn is_valid_letter_slice(slc: &[u8]) -> bool { + let mut index = 0; + while index < slc.len() { + if !is_valid_letter(slc[index]) { + return false; + } + index += 1; + } + true +} diff --git a/rust/vendor/lexical-util/src/assert.rs b/rust/vendor/lexical-util/src/assert.rs new file mode 100644 index 0000000..881db49 --- /dev/null +++ b/rust/vendor/lexical-util/src/assert.rs @@ -0,0 +1,57 @@ +//! Debugging assertions to check a radix is valid. + +#![doc(hidden)] + +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +use crate::constants::FormattedSize; + +// RADIX + +/// Check radix is in range `[2, 36]` in debug builds. +#[inline(always)] +#[cfg(feature = "radix")] +pub fn debug_assert_radix(radix: u32) { + debug_assert!((2..=36).contains(&radix), "Numerical base must be from 2-36."); +} + +/// Check radix is is 10 or a power of 2. +#[inline(always)] +#[cfg(all(feature = "power-of-two", not(feature = "radix")))] +pub fn debug_assert_radix(radix: u32) { + debug_assert!(matches!(radix, 2 | 4 | 8 | 10 | 16 | 32), "Numerical base must be from 2-36."); +} + +/// Check radix is equal to 10. +#[inline(always)] +#[cfg(not(feature = "power-of-two"))] +pub fn debug_assert_radix(radix: u32) { + debug_assert!(radix == 10, "Numerical base must be 10."); +} + +// BUFFER + +/// Assertion the buffer has sufficient room for the output. +#[inline(always)] +#[cfg(all(feature = "power-of-two", any(feature = "write-floats", feature = "write-integers")))] +pub const fn assert_buffer(radix: u32, len: usize) { + assert!( + match radix { + 10 => len >= T::FORMATTED_SIZE_DECIMAL, + _ => len >= T::FORMATTED_SIZE, + }, + "Buffer is too small: may overwrite buffer, panicking!" + ); +} + +/// Assertion the buffer has sufficient room for the output. +#[inline(always)] +#[cfg(all( + not(feature = "power-of-two"), + any(feature = "write-floats", feature = "write-integers") +))] +pub const fn assert_buffer(_: u32, len: usize) { + assert!( + len >= T::FORMATTED_SIZE_DECIMAL, + "Buffer is too small: may overwrite buffer, panicking!" + ); +} diff --git a/rust/vendor/lexical-util/src/bf16.rs b/rust/vendor/lexical-util/src/bf16.rs new file mode 100644 index 0000000..b3da276 --- /dev/null +++ b/rust/vendor/lexical-util/src/bf16.rs @@ -0,0 +1,9 @@ +//! Brain Floating Point implementation, a 16-bit type used in machine learning. +//! +//! bf16 is meant as an interchange format, and therefore there may be +//! rounding error in using it for fast-path algorithms. Since there +//! are no native operations using `bf16`, this is of minimal concern. + +#![cfg(feature = "f16")] + +pub use float16::bf16; diff --git a/rust/vendor/lexical-util/src/constants.rs b/rust/vendor/lexical-util/src/constants.rs new file mode 100644 index 0000000..1e6b0c0 --- /dev/null +++ b/rust/vendor/lexical-util/src/constants.rs @@ -0,0 +1,122 @@ +//! Pre-defined constants for numeric types. + +#![doc(hidden)] +#![cfg(any(feature = "write-floats", feature = "write-integers"))] + +#[cfg(feature = "f16")] +use crate::bf16::bf16; +#[cfg(feature = "f16")] +use crate::f16::f16; + +/// The size, in bytes, of formatted values. +pub trait FormattedSize { + /// Maximum number of bytes required to serialize a number to string. + /// If [`power-of-two`] or [`radix`] is not enabled, this is the same as + /// [`FORMATTED_SIZE_DECIMAL`][`Self::FORMATTED_SIZE_DECIMAL`]. + /// + ///
+ /// + /// Note that this value may be insufficient if digit precision control, + /// exponent break points, or disabling exponent notation is used. If + /// you are changing the number significant digits written, the exponent + /// break points, or disabling scientific notation, you will need a larger + /// buffer than the one provided. An upper limit on the buffer size can + /// then be determined using [`WriteOptions::buffer_size`]. + /// + /// Using an insufficiently large buffer will lead to the code panicking. + /// + ///
+ /// + /// [`WriteOptions::buffer_size`]: crate::options::WriteOptions::buffer_size + /// [`lexical_write_float`]: https://github.com/Alexhuszagh/rust-lexical/tree/main/lexical-write-float + /// [`power-of-two`]: crate#features + /// [`radix`]: crate#features + const FORMATTED_SIZE: usize; + + /// Maximum number of bytes required to serialize a number to a decimal + /// string. + /// + ///
+ /// + /// Note that this value may be insufficient if digit precision control, + /// exponent break points, or disabling exponent notation is used. If + /// you are changing the number significant digits written, the exponent + /// break points, or disabling scientific notation, you will need a larger + /// buffer than the one provided. An upper limit on the buffer size can + /// then be determined using [`WriteOptions::buffer_size`]. + /// + /// Using an insufficiently large buffer will lead to the code panicking. + /// + ///
+ /// + /// [`WriteOptions::buffer_size`]: crate::options::WriteOptions::buffer_size + /// [`lexical_write_float`]: https://github.com/Alexhuszagh/rust-lexical/tree/main/lexical-write-float + const FORMATTED_SIZE_DECIMAL: usize; +} + +macro_rules! formatted_size_impl { + ($($t:tt $decimal:literal $radix:literal ; )*) => ($( + impl FormattedSize for $t { + #[cfg(feature = "power-of-two")] + const FORMATTED_SIZE: usize = $radix; + #[cfg(not(feature = "power-of-two"))] + const FORMATTED_SIZE: usize = $decimal; + const FORMATTED_SIZE_DECIMAL: usize = $decimal; + } + )*); +} + +formatted_size_impl! { + i8 4 16 ; + i16 6 32 ; + i32 11 64 ; + i64 20 128 ; + i128 40 256 ; + u8 3 16 ; + u16 5 32 ; + u32 10 64 ; + u64 20 128 ; + u128 39 256 ; + // The f64 buffer is actually a size of 60, but use 64 since it's a power of 2. + // Use 256 for non-decimal values, actually, since we seem to have memory + // issues with f64. Clearly not sufficient memory allocated for non-decimal + // values. + //bf16 64 256 ; + //f16 64 256 ; + f32 64 256 ; + f64 64 256 ; + //f128 128 512 ; + //f256 256 1024 ; +} + +#[cfg(feature = "f16")] +formatted_size_impl! { + f16 64 256 ; + bf16 64 256 ; +} + +#[cfg(target_pointer_width = "16")] +formatted_size_impl! { isize 6 32 ; } +#[cfg(target_pointer_width = "16")] +formatted_size_impl! { usize 5 32 ; } + +#[cfg(target_pointer_width = "32")] +formatted_size_impl! { isize 11 64 ; } +#[cfg(target_pointer_width = "32")] +formatted_size_impl! { usize 10 64 ; } + +#[cfg(target_pointer_width = "64")] +formatted_size_impl! { isize 20 128 ; } +#[cfg(target_pointer_width = "64")] +formatted_size_impl! { usize 20 128 ; } + +/// Maximum number of bytes required to serialize any number with default +/// options to string. +/// +/// Note that this value may be insufficient if digit precision control, +/// exponent break points, or disabling exponent notation is used. +/// Please read the documentation in [`lexical_write_float`] for more +/// information. +/// +/// [`lexical_write_float`]: https://github.com/Alexhuszagh/rust-lexical/tree/main/lexical-write-float +pub const BUFFER_SIZE: usize = f64::FORMATTED_SIZE; diff --git a/rust/vendor/lexical-util/src/digit.rs b/rust/vendor/lexical-util/src/digit.rs new file mode 100644 index 0000000..6bb08fe --- /dev/null +++ b/rust/vendor/lexical-util/src/digit.rs @@ -0,0 +1,115 @@ +//! Utilities to process digits. +//! +//! This both contains routines to convert to and from digits, +//! as well as iterate over digits while skipping digit separators. + +// CONST FNS +// --------- + +// These are optimized functions for when the radix is known at compile-time, +// which is **most** of our cases. There are cases where for code generation, +// using a runtime algorithm is preferable. + +/// Unchecked, highly optimized algorithm to convert a char to a digit. +/// This only works if the input character is known to be a valid digit. +#[inline(always)] +pub const fn char_to_valid_digit_const(c: u8, radix: u32) -> u32 { + if radix <= 10 { + // Optimize for small radixes. + (c.wrapping_sub(b'0')) as u32 + } else { + // Fallback, still decently fast. + let digit = match c { + b'0'..=b'9' => c - b'0', + b'A'..=b'Z' => c - b'A' + 10, + b'a'..=b'z' => c - b'a' + 10, + _ => 0xFF, + }; + digit as u32 + } +} + +/// Convert a character to a digit with a radix known at compile time. +/// +/// This optimizes for cases where radix is <= 10, and uses a decent, +/// match-based fallback algorithm. +#[inline(always)] +pub const fn char_to_digit_const(c: u8, radix: u32) -> Option { + let digit = char_to_valid_digit_const(c, radix); + if digit < radix { + Some(digit) + } else { + None + } +} + +/// Determine if a character is a digit with a radix known at compile time. +#[inline(always)] +pub const fn char_is_digit_const(c: u8, radix: u32) -> bool { + char_to_digit_const(c, radix).is_some() +} + +/// Convert a digit to a character with a radix known at compile time. +/// +/// This optimizes for cases where radix is <= 10, and uses a decent, +/// match-based fallback algorithm. +#[inline(always)] +#[cfg(any(feature = "parse-floats", feature = "write-floats", feature = "write-integers"))] +pub const fn digit_to_char_const(digit: u32, radix: u32) -> u8 { + if radix <= 10 || digit < 10 { + // Can short-circuit if we know the radix is small at compile time. + digit as u8 + b'0' + } else { + digit as u8 + b'A' - 10 + } +} + +// NON-CONST +// --------- + +// These are less optimized functions for when the radix is not known at +// compile-time, which is a few (but important) cases. These generally have +// improved compiler optimization passes when generics are used more sparingly. + +/// Convert a character to a digit. +#[inline(always)] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] +pub const fn char_to_digit(c: u8, radix: u32) -> Option { + // Fallback, still decently fast. + let digit = match c { + b'0'..=b'9' => c - b'0', + b'A'..=b'Z' => c - b'A' + 10, + b'a'..=b'z' => c - b'a' + 10, + _ => 0xFF, + } as u32; + if digit < radix { + Some(digit) + } else { + None + } +} + +/// Determine if a character is a digit. +#[inline(always)] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] +pub const fn char_is_digit(c: u8, radix: u32) -> bool { + char_to_digit(c, radix).is_some() +} + +/// Convert a digit to a character. This uses a pre-computed table to avoid +/// branching. +/// +/// # Panics +/// +/// Panics if `digit >= 36`. +#[inline(always)] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +pub fn digit_to_char(digit: u32) -> u8 { + const TABLE: [u8; 36] = [ + b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', + b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', + b'U', b'V', b'W', b'X', b'Y', b'Z', + ]; + debug_assert!(digit < 36, "digit_to_char() invalid character."); + TABLE[digit as usize] +} diff --git a/rust/vendor/lexical-util/src/div128.rs b/rust/vendor/lexical-util/src/div128.rs new file mode 100644 index 0000000..717793d --- /dev/null +++ b/rust/vendor/lexical-util/src/div128.rs @@ -0,0 +1,512 @@ +//! Optimized division algorithms for u128. +//! +//! # Fast Algorithms +//! +//! The more optimized algorithms for calculating the divisor constants are +//! based off of the paper "Division by Invariant Integers Using +//! Multiplication", by T. Granlund and P. Montgomery, in "Proc. of the +//! SIGPLAN94 Conference on Programming Language Design and Implementation", +//! available online [here](https://gmplib.org/~tege/divcnst-pldi94.pdf). +//! +//! This approach is derived from the Rust algorithm for formatting 128-bit +//! values, and therefore is similarly dual-licensed under MIT/Apache-2.0. +//! +//! # Fallback Algorithms +//! +//! The slower algorithms in this module are derived off of `dtolnay/itoa` +//! and Rust's compiler-builtins crate. This copies a specific +//! path of LLVM's `__udivmodti4` intrinsic, which does division/ +//! modulus for u128 in a single step. Rust implements both division +//! and modulus in terms of this intrinsic, but calls the intrinsic +//! twice for subsequent division and modulus operations on the same +//! dividend/divisor, leading to significant performance overhead. +//! +//! This module calculates the optimal divisors for each radix, +//! and exports a general-purpose division algorithm for u128 where +//! the divisor can fit in a u64. The moderate algorithm is derived from +//! dtolnay/itoa, which can be found +//! [here](https://github.com/dtolnay/itoa/blob/master/src/udiv128.rs), which +//! in turn is derived from Rust's compiler-builtins crate, which can be found +//! [here](https://github.com/rust-lang-nursery/compiler-builtins/blob/master/src/int/udiv.rs). +//! +//! Licensing for these routines is therefore subject to an MIT/Illinois +//! dual license (a BSD-like license), while the rest of the module is +//! subject to an MIT/Apache-2.0 dual-license. +//! +//! # Generation +//! +//! See [`etc/div128.py`] for the script to generate the divisors and the +//! constants, and the division algorithm. +//! +//! [`etc/div128.py`]: https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-util/etc/div128.py + +#![cfg(not(feature = "compact"))] +#![cfg(any(feature = "write-floats", feature = "write-integers"))] + +use crate::assert::debug_assert_radix; +use crate::mul::mulhi; + +/// Calculate a div/remainder algorithm optimized for power-of-two radixes. +/// +/// This is trivial: the number of digits we process is `64 / log2(radix)`. +/// Therefore, the `shr` is `log2(radix) * digits`, and the mask is just the +/// lower `shr` bits of the digits. +#[inline(always)] +#[allow(clippy::many_single_char_names)] // reason="mathematical names" +pub const fn pow2_u128_divrem(n: u128, mask: u64, shr: u32) -> (u128, u64) { + let quot = n >> shr; + let rem = mask & n as u64; + (quot, rem) +} + +/// Fast division/remainder algorithm for u128, without a fast native +/// approximation. +#[inline(always)] +#[allow(clippy::many_single_char_names)] // reason="mathematical names" +pub fn fast_u128_divrem( + n: u128, + d: u64, + fast: u128, + fast_shr: u32, + factor: u128, + factor_shr: u32, +) -> (u128, u64) { + let quot = if n < fast { + ((n >> fast_shr) as u64 / (d >> fast_shr)) as u128 + } else { + mulhi::(n, factor) >> factor_shr + }; + let rem = (n - quot * d as u128) as u64; + (quot, rem) +} + +/// Fast division/remainder algorithm for u128, without a fast native +/// approximation. +#[inline(always)] +#[allow(clippy::many_single_char_names)] // reason="mathematical names" +pub fn moderate_u128_divrem(n: u128, d: u64, factor: u128, factor_shr: u32) -> (u128, u64) { + let quot = mulhi::(n, factor) >> factor_shr; + let rem = (n - quot * d as u128) as u64; + (quot, rem) +} + +/// Optimized fallback division/remainder algorithm for u128. +/// +/// This is because the code generation for u128 divrem is very inefficient +/// in Rust, calling both `__udivmodti4` twice internally, rather than a single +/// time. +/// +/// This is still a fair bit slower than the optimized algorithms described +/// in the above paper, but this is a suitable fallback when we cannot use +/// the faster algorithm. +#[cfg_attr(not(feature = "compact"), inline(always))] +#[allow(clippy::many_single_char_names)] // reason="mathematical names" +pub fn slow_u128_divrem(n: u128, d: u64, d_ctlz: u32) -> (u128, u64) { + // Ensure we have the correct number of leading zeros passed. + debug_assert_eq!(d_ctlz, d.leading_zeros()); + + // Optimize if we can divide using u64 first. + let high = (n >> 64) as u64; + if high == 0 { + let low = n as u64; + return ((low / d) as u128, low % d); + } + + // sr = 1 + u64::BITS + d.leading_zeros() - high.leading_zeros(); + let sr = 65 + d_ctlz - high.leading_zeros(); + + // 1 <= sr <= u64::BITS - 1 + let mut q: u128 = n << (128 - sr); + let mut r: u128 = n >> sr; + let mut carry: u64 = 0; + + // Don't use a range because they may generate references to memcpy in + // unoptimized code Loop invariants: r < d; carry is 0 or 1 + let mut i = 0; + while i < sr { + i += 1; + + // r:q = ((r:q) << 1) | carry + r = (r << 1) | (q >> 127); + q = (q << 1) | carry as u128; + + // carry = 0 + // if r >= d { + // r -= d; + // carry = 1; + // } + let s = (d as u128).wrapping_sub(r).wrapping_sub(1) as i128 >> 127; + carry = (s & 1) as u64; + r -= (d as u128) & s as u128; + } + + ((q << 1) | carry as u128, r as u64) +} + +/// Calculate the div/remainder of a value based on the radix. +/// +/// This uses the largest divisor possible for the given size, +/// and uses various fast-path approximations for different types. +/// +/// 1. Powers-of-two can be cleanly split into 2 64-bit products. +/// 2. Division that can be simulated as if by multiplication by a constant. +/// 3. Cases of 2. with a power-of-two divisor. +/// 4. Fallback cases. +/// +/// This returns the quotient and the remainder. +/// For the number of digits processed, see +/// [`min_step`](crate::step::min_step). +#[inline(always)] +#[allow(clippy::needless_return)] // reason="required based on radix configuration" +pub fn u128_divrem(n: u128, radix: u32) -> (u128, u64) { + debug_assert_radix(radix); + + // NOTE: to avoid branching when w don't need it, we use the compile logic + + #[cfg(feature = "radix")] + { + return match radix { + 2 => u128_divrem_2(n), + 3 => u128_divrem_3(n), + 4 => u128_divrem_4(n), + 5 => u128_divrem_5(n), + 6 => u128_divrem_6(n), + 7 => u128_divrem_7(n), + 8 => u128_divrem_8(n), + 9 => u128_divrem_9(n), + 10 => u128_divrem_10(n), + 11 => u128_divrem_11(n), + 12 => u128_divrem_12(n), + 13 => u128_divrem_13(n), + 14 => u128_divrem_14(n), + 15 => u128_divrem_15(n), + 16 => u128_divrem_16(n), + 17 => u128_divrem_17(n), + 18 => u128_divrem_18(n), + 19 => u128_divrem_19(n), + 20 => u128_divrem_20(n), + 21 => u128_divrem_21(n), + 22 => u128_divrem_22(n), + 23 => u128_divrem_23(n), + 24 => u128_divrem_24(n), + 25 => u128_divrem_25(n), + 26 => u128_divrem_26(n), + 27 => u128_divrem_27(n), + 28 => u128_divrem_28(n), + 29 => u128_divrem_29(n), + 30 => u128_divrem_30(n), + 31 => u128_divrem_31(n), + 32 => u128_divrem_32(n), + 33 => u128_divrem_33(n), + 34 => u128_divrem_34(n), + 35 => u128_divrem_35(n), + 36 => u128_divrem_36(n), + _ => unreachable!(), + }; + } + + #[cfg(all(feature = "power-of-two", not(feature = "radix")))] + { + return match radix { + 2 => u128_divrem_2(n), + 4 => u128_divrem_4(n), + 8 => u128_divrem_8(n), + 10 => u128_divrem_10(n), + 16 => u128_divrem_16(n), + 32 => u128_divrem_32(n), + _ => unreachable!(), + }; + } + + #[cfg(not(feature = "power-of-two"))] + { + return u128_divrem_10(n); + } +} + +// AUTO-GENERATED +// These functions were auto-generated by `etc/div128.py`. +// Do not edit them unless there is a good reason to. +// Preferably, edit the source code to generate the constants. +// +// The seemingly magical values are all derived there, and are explained +// in the function signatures of the functions they call. + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn u128_divrem_2(n: u128) -> (u128, u64) { + pow2_u128_divrem(n, 18446744073709551615, 64) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_3(n: u128) -> (u128, u64) { + slow_u128_divrem(n, 12157665459056928801, 0) +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn u128_divrem_4(n: u128) -> (u128, u64) { + pow2_u128_divrem(n, 18446744073709551615, 64) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_5(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 7450580596923828125, 105312291668557186697918027683670432319, 61) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_6(n: u128) -> (u128, u64) { + fast_u128_divrem( + n, + 4738381338321616896, + 309485009821345068724781056, + 24, + 165591931273573223021296166324748699891, + 61, + ) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_7(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 3909821048582988049, 200683792729517998822275406364627986707, 61) +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn u128_divrem_8(n: u128) -> (u128, u64) { + pow2_u128_divrem(n, 9223372036854775807, 63) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_9(n: u128) -> (u128, u64) { + slow_u128_divrem(n, 12157665459056928801, 0) +} + +#[inline(always)] +fn u128_divrem_10(n: u128) -> (u128, u64) { + fast_u128_divrem( + n, + 10000000000000000000, + 9671406556917033397649408, + 19, + 156927543384667019095894735580191660403, + 62, + ) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_11(n: u128) -> (u128, u64) { + slow_u128_divrem(n, 5559917313492231481, 1) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_12(n: u128) -> (u128, u64) { + slow_u128_divrem(n, 2218611106740436992, 3) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_13(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 8650415919381337933, 181410402513790565292660635782582404765, 62) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_14(n: u128) -> (u128, u64) { + fast_u128_divrem( + n, + 2177953337809371136, + 1208925819614629174706176, + 16, + 1407280417134467544760816054546363235, + 53, + ) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_15(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 6568408355712890625, 1866504587258795246613513364166764993, 55) +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn u128_divrem_16(n: u128) -> (u128, u64) { + pow2_u128_divrem(n, 18446744073709551615, 64) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_17(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 2862423051509815793, 68529153692836345537218837732158950089, 59) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_18(n: u128) -> (u128, u64) { + fast_u128_divrem( + n, + 6746640616477458432, + 604462909807314587353088, + 15, + 232601011830094623283686247347795155951, + 62, + ) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_19(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 15181127029874798299, 25842538415601616733690423925257626679, 60) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_20(n: u128) -> (u128, u64) { + fast_u128_divrem( + n, + 1638400000000000000, + 4951760157141521099596496896, + 28, + 239452428260295134118491722992235809941, + 60, + ) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_21(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 3243919932521508681, 120939747781233590383781714337497669585, 60) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_22(n: u128) -> (u128, u64) { + slow_u128_divrem(n, 6221821273427820544, 1) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_23(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 11592836324538749809, 270731922700393644432243678371210997949, 63) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_24(n: u128) -> (u128, u64) { + fast_u128_divrem( + n, + 876488338465357824, + 10141204801825835211973625643008, + 39, + 55950381945266105153185943557606235389, + 57, + ) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_25(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 1490116119384765625, 131640364585696483372397534604588040399, 59) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_26(n: u128) -> (u128, u64) { + fast_u128_divrem( + n, + 2481152873203736576, + 151115727451828646838272, + 13, + 316239166637962178669658228673482425689, + 61, + ) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_27(n: u128) -> (u128, u64) { + slow_u128_divrem(n, 4052555153018976267, 2) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_28(n: u128) -> (u128, u64) { + fast_u128_divrem( + n, + 6502111422497947648, + 1237940039285380274899124224, + 26, + 241348591538561183926479953354701294803, + 62, + ) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_29(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 10260628712958602189, 152941450056053853841698190746050519297, 62) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_30(n: u128) -> (u128, u64) { + slow_u128_divrem(n, 15943230000000000000, 0) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_31(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 787662783788549761, 124519929891402176328714857711808162537, 58) +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn u128_divrem_32(n: u128) -> (u128, u64) { + pow2_u128_divrem(n, 1152921504606846975, 60) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_33(n: u128) -> (u128, u64) { + slow_u128_divrem(n, 1667889514952984961, 3) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_34(n: u128) -> (u128, u64) { + fast_u128_divrem( + n, + 2386420683693101056, + 75557863725914323419136, + 12, + 328792707121977505492535302517672775183, + 61, + ) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_35(n: u128) -> (u128, u64) { + moderate_u128_divrem(n, 3379220508056640625, 116097442450503652080238494022501325491, 60) +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +fn u128_divrem_36(n: u128) -> (u128, u64) { + fast_u128_divrem( + n, + 4738381338321616896, + 309485009821345068724781056, + 24, + 165591931273573223021296166324748699891, + 61, + ) +} diff --git a/rust/vendor/lexical-util/src/error.rs b/rust/vendor/lexical-util/src/error.rs new file mode 100644 index 0000000..53c2cad --- /dev/null +++ b/rust/vendor/lexical-util/src/error.rs @@ -0,0 +1,423 @@ +//! Error type for numeric parsing functions. +//! +//! The error type is C-compatible, simplifying use external language +//! bindings. + +#![doc(hidden)] + +use core::fmt; +#[cfg(feature = "std")] +use std::error; + +/// Error code during parsing, indicating failure type. +#[non_exhaustive] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub enum Error { + // PARSE ERRORS + /// Integral overflow occurred during numeric parsing. + Overflow(usize), + /// Integral underflow occurred during numeric parsing. + Underflow(usize), + /// Invalid digit found before string termination. + InvalidDigit(usize), + /// Empty byte array found. + Empty(usize), + /// Empty mantissa found. + EmptyMantissa(usize), + /// Empty exponent found. + EmptyExponent(usize), + /// Empty integer found. + EmptyInteger(usize), + /// Empty fraction found. + EmptyFraction(usize), + /// Invalid positive mantissa sign was found. + InvalidPositiveMantissaSign(usize), + /// Mantissa sign was required(usize), but not found. + MissingMantissaSign(usize), + /// Exponent was present but not allowed. + InvalidExponent(usize), + /// Invalid positive exponent sign was found. + InvalidPositiveExponentSign(usize), + /// Exponent sign was required(usize), but not found. + MissingExponentSign(usize), + /// Exponent was present without fraction component. + ExponentWithoutFraction(usize), + /// Integer or integer component of float had invalid leading zeros. + InvalidLeadingZeros(usize), + /// No exponent with required exponent notation. + MissingExponent(usize), + /// Integral sign was required(usize), but not found. + MissingSign(usize), + /// Invalid positive sign for an integer was found. + InvalidPositiveSign(usize), + /// Invalid negative sign for an unsigned type was found. + InvalidNegativeSign(usize), + + // NUMBER FORMAT ERRORS + /// Invalid radix for the mantissa (significant) digits. + InvalidMantissaRadix, + /// Invalid base for the exponent. + InvalidExponentBase, + /// Invalid radix for the exponent digits. + InvalidExponentRadix, + /// Invalid digit separator character. + InvalidDigitSeparator, + /// Invalid decimal point character. + InvalidDecimalPoint, + /// Invalid symbol to represent exponent notation. + InvalidExponentSymbol, + /// Invalid character for a base prefix. + InvalidBasePrefix, + /// Invalid character for a base suffix. + InvalidBaseSuffix, + /// Invalid punctuation characters: multiple symbols overlap. + InvalidPunctuation, + /// Optional exponent flags were set while disabling exponent notation. + InvalidExponentFlags, + /// Set no positive mantissa sign while requiring mantissa signs. + InvalidMantissaSign, + /// Set no positive exponent sign while requiring exponent signs. + InvalidExponentSign, + /// Set optional special float flags while disable special floats. + InvalidSpecial, + /// Invalid consecutive integer digit separator. + InvalidConsecutiveIntegerDigitSeparator, + /// Invalid consecutive fraction digit separator. + InvalidConsecutiveFractionDigitSeparator, + /// Invalid consecutive exponent digit separator. + InvalidConsecutiveExponentDigitSeparator, + /// Invalid flags were set without the format feature. + InvalidFlags, + + // OPTION ERRORS + /// Invalid NaN string: must start with an `n` character. + InvalidNanString, + /// NaN string is too long. + NanStringTooLong, + /// Invalid short infinity string: must start with an `i` character. + InvalidInfString, + /// Short infinity string is too long. + InfStringTooLong, + /// Invalid long infinity string: must start with an `i` character. + InvalidInfinityString, + /// Long infinity string is too long. + InfinityStringTooLong, + /// Long infinity string is too short: it must be as long as short infinity. + InfinityStringTooShort, + /// Invalid float parsing algorithm. + InvalidFloatParseAlgorithm, + /// Invalid radix for the significant digits. + InvalidRadix, + /// Invalid precision flags for writing floats. + InvalidFloatPrecision, + /// Invalid negative exponent break: break is above 0. + InvalidNegativeExponentBreak, + /// Invalid positive exponent break: break is below 0. + InvalidPositiveExponentBreak, + + // NOT AN ERROR + /// An error did not actually occur, and the result was successful. + Success, +} + +macro_rules! is_error_type { + ($name:ident, $type:ident$($t:tt)*) => ( + /// const fn check to see if an error is of a specific type. + pub const fn $name(&self) -> bool { + // Note: enum equality is not a const fn, so use a let expression. + if let Self::$type$($t)* = self { + true + } else { + false + } + } + ); +} + +impl Error { + /// Get a description of the error in a const, panic friendly way. + #[inline] + pub const fn description(&self) -> &'static str { + match self { + // PARSE ERRORS + Self::Overflow(_) => "'numeric overflow occurred'", + Self::Underflow(_) => "'numeric underflow occurred'", + Self::InvalidDigit(_) => "'invalid digit found'", + Self::Empty(_) => "'the string to parse was empty'", + Self::EmptyMantissa(_) => "'no significant digits found'", + Self::EmptyExponent(_) => "'exponent notation found without an exponent'", + Self::EmptyInteger(_) => "'invalid float with no integer digits'", + Self::EmptyFraction(_) => "'invalid float with no fraction digits'", + Self::InvalidPositiveMantissaSign(_) => "'invalid `+` sign before significant digits'", + Self::MissingMantissaSign(_) => "'missing required `+/-` sign for significant digits'", + Self::InvalidExponent(_) => "'exponent found but not allowed'", + Self::InvalidPositiveExponentSign(_) => "'invalid `+` sign in exponent'", + Self::MissingExponentSign(_) => "'missing required `+/-` sign for exponent'", + Self::ExponentWithoutFraction(_) => "'invalid float containing exponent without fraction'", + Self::InvalidLeadingZeros(_) => "'invalid number with leading zeros before digits'", + Self::MissingExponent(_) => "'missing required exponent'", + Self::MissingSign(_) => "'missing required `+/-` sign for integer'", + Self::InvalidPositiveSign(_) => "'invalid `+` sign for an integer was found'", + Self::InvalidNegativeSign(_) => "'invalid `-` sign for an unsigned type was found'", + + // NUMBER FORMAT ERRORS + Self::InvalidMantissaRadix => "'invalid radix for mantissa digits'", + Self::InvalidExponentBase => "'invalid exponent base'", + Self::InvalidExponentRadix => "'invalid radix for exponent digits'", + Self::InvalidDigitSeparator => "'invalid digit separator: must be ASCII and not a digit or a `+/-` sign'", + Self::InvalidDecimalPoint => "'invalid decimal point: must be ASCII and not a digit or a `+/-` sign'", + Self::InvalidExponentSymbol => "'invalid exponent symbol: must be ASCII and not a digit or a `+/-` sign'", + Self::InvalidBasePrefix => "'invalid base prefix character'", + Self::InvalidBaseSuffix => "'invalid base suffix character'", + Self::InvalidPunctuation => "'invalid punctuation: multiple characters overlap'", + Self::InvalidExponentFlags => "'exponent flags set while disabling exponent notation'", + Self::InvalidMantissaSign => "'disabled the `+` sign while requiring a sign for significant digits'", + Self::InvalidExponentSign => "'disabled the `+` sign while requiring a sign for exponent digits'", + Self::InvalidSpecial => "'special flags set while disabling special floats'", + Self::InvalidConsecutiveIntegerDigitSeparator => "'enabled consecutive digit separators in the integer without setting a valid location'", + Self::InvalidConsecutiveFractionDigitSeparator => "'enabled consecutive digit separators in the fraction without setting a valid location'", + Self::InvalidConsecutiveExponentDigitSeparator => "'enabled consecutive digit separators in the exponent without setting a valid location'", + Self::InvalidFlags => "'invalid flags enabled without the format feature'", + + // OPTION ERRORS + Self::InvalidNanString => "'NaN string must started with `n`'", + Self::NanStringTooLong => "'NaN string is too long'", + Self::InvalidInfString => "'short infinity string must started with `i`'", + Self::InfStringTooLong => "'short infinity string is too long'", + Self::InvalidInfinityString => "'long infinity string must started with `i`'", + Self::InfinityStringTooLong => "'long infinity string is too long'", + Self::InfinityStringTooShort => "'long infinity string is too short'", + Self::InvalidFloatParseAlgorithm => "'invalid combination of float parse algorithms'", + Self::InvalidRadix => "'invalid radix for significant digits'", + Self::InvalidFloatPrecision => "'invalid float precision: min digits is larger than max digits'", + Self::InvalidNegativeExponentBreak => "'invalid negative exponent break: value is above 0'", + Self::InvalidPositiveExponentBreak => "'invalid positive exponent break: value is below 0'", + + // NOT AN ERROR + Self::Success => "'not actually an error'", + } + } + + /// Get the index for the parsing error. + #[inline] + pub fn index(&self) -> Option<&usize> { + match self { + // PARSE ERRORS + Self::Overflow(index) => Some(index), + Self::Underflow(index) => Some(index), + Self::InvalidDigit(index) => Some(index), + Self::Empty(index) => Some(index), + Self::EmptyMantissa(index) => Some(index), + Self::EmptyExponent(index) => Some(index), + Self::EmptyInteger(index) => Some(index), + Self::EmptyFraction(index) => Some(index), + Self::InvalidPositiveMantissaSign(index) => Some(index), + Self::MissingMantissaSign(index) => Some(index), + Self::InvalidExponent(index) => Some(index), + Self::InvalidPositiveExponentSign(index) => Some(index), + Self::MissingExponentSign(index) => Some(index), + Self::ExponentWithoutFraction(index) => Some(index), + Self::InvalidLeadingZeros(index) => Some(index), + Self::MissingExponent(index) => Some(index), + Self::MissingSign(index) => Some(index), + Self::InvalidPositiveSign(index) => Some(index), + Self::InvalidNegativeSign(index) => Some(index), + + // NUMBER FORMAT ERRORS + Self::InvalidMantissaRadix => None, + Self::InvalidExponentBase => None, + Self::InvalidExponentRadix => None, + Self::InvalidDigitSeparator => None, + Self::InvalidDecimalPoint => None, + Self::InvalidExponentSymbol => None, + Self::InvalidBasePrefix => None, + Self::InvalidBaseSuffix => None, + Self::InvalidPunctuation => None, + Self::InvalidExponentFlags => None, + Self::InvalidMantissaSign => None, + Self::InvalidExponentSign => None, + Self::InvalidSpecial => None, + Self::InvalidConsecutiveIntegerDigitSeparator => None, + Self::InvalidConsecutiveFractionDigitSeparator => None, + Self::InvalidConsecutiveExponentDigitSeparator => None, + Self::InvalidFlags => None, + + // OPTION ERRORS + Self::InvalidNanString => None, + Self::NanStringTooLong => None, + Self::InvalidInfString => None, + Self::InfStringTooLong => None, + Self::InvalidInfinityString => None, + Self::InfinityStringTooLong => None, + Self::InfinityStringTooShort => None, + Self::InvalidFloatParseAlgorithm => None, + Self::InvalidRadix => None, + Self::InvalidFloatPrecision => None, + Self::InvalidNegativeExponentBreak => None, + Self::InvalidPositiveExponentBreak => None, + + // NOT AN ERROR + Self::Success => None, + } + } + + is_error_type!(is_overflow, Overflow(_)); + is_error_type!(is_underflow, Underflow(_)); + is_error_type!(is_invalid_digit, InvalidDigit(_)); + is_error_type!(is_empty, Empty(_)); + is_error_type!(is_empty_mantissa, EmptyMantissa(_)); + is_error_type!(is_empty_exponent, EmptyExponent(_)); + is_error_type!(is_empty_integer, EmptyInteger(_)); + is_error_type!(is_empty_fraction, EmptyFraction(_)); + is_error_type!(is_invalid_positive_mantissa_sign, InvalidPositiveMantissaSign(_)); + is_error_type!(is_missing_mantissa_sign, MissingMantissaSign(_)); + is_error_type!(is_invalid_exponent, InvalidExponent(_)); + is_error_type!(is_invalid_positive_exponent_sign, InvalidPositiveExponentSign(_)); + is_error_type!(is_missing_exponent_sign, MissingExponentSign(_)); + is_error_type!(is_exponent_without_fraction, ExponentWithoutFraction(_)); + is_error_type!(is_invalid_leading_zeros, InvalidLeadingZeros(_)); + is_error_type!(is_missing_exponent, MissingExponent(_)); + is_error_type!(is_missing_sign, MissingSign(_)); + is_error_type!(is_invalid_positive_sign, InvalidPositiveSign(_)); + is_error_type!(is_invalid_negative_sign, InvalidNegativeSign(_)); + is_error_type!(is_invalid_mantissa_radix, InvalidMantissaRadix); + is_error_type!(is_invalid_exponent_base, InvalidExponentBase); + is_error_type!(is_invalid_exponent_radix, InvalidExponentRadix); + is_error_type!(is_invalid_digit_separator, InvalidDigitSeparator); + is_error_type!(is_invalid_decimal_point, InvalidDecimalPoint); + is_error_type!(is_invalid_exponent_symbol, InvalidExponentSymbol); + is_error_type!(is_invalid_base_prefix, InvalidBasePrefix); + is_error_type!(is_invalid_base_suffix, InvalidBaseSuffix); + is_error_type!(is_invalid_punctuation, InvalidPunctuation); + is_error_type!(is_invalid_exponent_flags, InvalidExponentFlags); + is_error_type!(is_invalid_mantissa_sign, InvalidMantissaSign); + is_error_type!(is_invalid_exponent_sign, InvalidExponentSign); + is_error_type!(is_invalid_special, InvalidSpecial); + is_error_type!( + is_invalid_consecutive_integer_digit_separator, + InvalidConsecutiveIntegerDigitSeparator + ); + is_error_type!( + is_invalid_consecutive_fraction_digit_separator, + InvalidConsecutiveFractionDigitSeparator + ); + is_error_type!( + is_invalid_consecutive_exponent_digit_separator, + InvalidConsecutiveExponentDigitSeparator + ); + is_error_type!(is_invalid_flags, InvalidFlags); + is_error_type!(is_invalid_nan_string, InvalidNanString); + is_error_type!(is_nan_string_too_long, NanStringTooLong); + is_error_type!(is_invalid_inf_string, InvalidInfString); + is_error_type!(is_inf_string_too_long, InfStringTooLong); + is_error_type!(is_invalid_infinity_string, InvalidInfinityString); + is_error_type!(is_infinity_string_too_long, InfinityStringTooLong); + is_error_type!(is_infinity_string_too_short, InfinityStringTooShort); + is_error_type!(is_invalid_float_parse_algorithm, InvalidFloatParseAlgorithm); + is_error_type!(is_invalid_radix, InvalidRadix); + is_error_type!(is_invalid_float_precision, InvalidFloatPrecision); + is_error_type!(is_invalid_negative_exponent_break, InvalidNegativeExponentBreak); + is_error_type!(is_invalid_positive_exponent_break, InvalidPositiveExponentBreak); + is_error_type!(is_success, Success); +} + +/// Add an error message for parsing errors. +macro_rules! write_parse_error { + ($formatter:ident, $message:expr, $index:ident) => { + write!($formatter, "lexical parse error: {} at index {}", $message, $index) + }; +} + +/// Add an error message for number format errors. +macro_rules! format_message { + ($formatter:ident, $message:expr) => { + write!($formatter, "lexical number format error: {}", $message) + }; +} + +/// Add an error message for options errors. +macro_rules! options_message { + ($formatter:ident, $message:expr) => { + write!($formatter, "lexical options error: {}", $message) + }; +} + +impl fmt::Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let description = self.description(); + match self { + // PARSE ERRORS + Self::Overflow(index) => write_parse_error!(formatter, description, index), + Self::Underflow(index) => write_parse_error!(formatter, description, index), + Self::InvalidDigit(index) => write_parse_error!(formatter, description, index), + Self::Empty(index) => write_parse_error!(formatter, description, index), + Self::EmptyMantissa(index) => write_parse_error!(formatter, description, index), + Self::EmptyExponent(index) => write_parse_error!(formatter, description, index), + Self::EmptyInteger(index) => write_parse_error!(formatter, description, index), + Self::EmptyFraction(index) => write_parse_error!(formatter, description, index), + Self::InvalidPositiveMantissaSign(index) => { + write_parse_error!(formatter, description, index) + }, + Self::MissingMantissaSign(index) => write_parse_error!(formatter, description, index), + Self::InvalidExponent(index) => write_parse_error!(formatter, description, index), + Self::InvalidPositiveExponentSign(index) => { + write_parse_error!(formatter, description, index) + }, + Self::MissingExponentSign(index) => write_parse_error!(formatter, description, index), + Self::ExponentWithoutFraction(index) => { + write_parse_error!(formatter, description, index) + }, + Self::InvalidLeadingZeros(index) => write_parse_error!(formatter, description, index), + Self::MissingExponent(index) => write_parse_error!(formatter, description, index), + Self::MissingSign(index) => write_parse_error!(formatter, description, index), + Self::InvalidPositiveSign(index) => write_parse_error!(formatter, description, index), + Self::InvalidNegativeSign(index) => write_parse_error!(formatter, description, index), + + // NUMBER FORMAT ERRORS + Self::InvalidMantissaRadix => format_message!(formatter, description), + Self::InvalidExponentBase => format_message!(formatter, description), + Self::InvalidExponentRadix => format_message!(formatter, description), + Self::InvalidDigitSeparator => format_message!(formatter, description), + Self::InvalidDecimalPoint => format_message!(formatter, description), + Self::InvalidExponentSymbol => format_message!(formatter, description), + Self::InvalidBasePrefix => format_message!(formatter, description), + Self::InvalidBaseSuffix => format_message!(formatter, description), + Self::InvalidPunctuation => format_message!(formatter, description), + Self::InvalidExponentFlags => format_message!(formatter, description), + Self::InvalidMantissaSign => format_message!(formatter, description), + Self::InvalidExponentSign => format_message!(formatter, description), + Self::InvalidSpecial => format_message!(formatter, description), + Self::InvalidConsecutiveIntegerDigitSeparator => { + format_message!(formatter, description) + }, + Self::InvalidConsecutiveFractionDigitSeparator => { + format_message!(formatter, description) + }, + Self::InvalidConsecutiveExponentDigitSeparator => { + format_message!(formatter, description) + }, + Self::InvalidFlags => format_message!(formatter, description), + + // OPTION ERRORS + Self::InvalidNanString => options_message!(formatter, description), + Self::NanStringTooLong => options_message!(formatter, description), + Self::InvalidInfString => options_message!(formatter, description), + Self::InfStringTooLong => options_message!(formatter, description), + Self::InvalidInfinityString => options_message!(formatter, description), + Self::InfinityStringTooLong => options_message!(formatter, description), + Self::InfinityStringTooShort => options_message!(formatter, description), + Self::InvalidFloatParseAlgorithm => options_message!(formatter, description), + Self::InvalidRadix => options_message!(formatter, description), + Self::InvalidFloatPrecision => options_message!(formatter, description), + Self::InvalidNegativeExponentBreak => options_message!(formatter, description), + Self::InvalidPositiveExponentBreak => options_message!(formatter, description), + + // NOT AN ERROR + Self::Success => write!(formatter, "{description}"), + } + } +} + +#[cfg(feature = "std")] +impl error::Error for Error { +} diff --git a/rust/vendor/lexical-util/src/extended_float.rs b/rust/vendor/lexical-util/src/extended_float.rs new file mode 100644 index 0000000..a609120 --- /dev/null +++ b/rust/vendor/lexical-util/src/extended_float.rs @@ -0,0 +1,39 @@ +//! Extended precision floating-point type. +//! +//! Also contains helpers to convert to and from native rust floats. +//! This representation stores the mantissa as a 64-bit unsigned integer, +//! and the exponent as a 32-bit unsigned integer, allowed ~80 bits of +//! precision (only 16 bits of the 32-bit integer are used, u32 is used +//! for performance). Since there is no storage for the sign bit, +//! this only works for positive floats. + +#![cfg(any(feature = "parse-floats", feature = "write-floats"))] + +use crate::num::UnsignedInteger; + +/// Extended precision floating-point type. +/// +/// This doesn't have any methods because it's used for **very** different +/// things for the Lemire, Bellepheron, and other algorithms. In Grisu, +/// it's an unbiased representation, for Lemire, it's a biased representation. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ExtendedFloat { + /// Mantissa for the extended-precision float. + pub mant: M, + /// Binary exponent for the extended-precision float. + pub exp: i32, +} + +impl ExtendedFloat { + /// Get the mantissa component. + #[inline(always)] + pub fn mantissa(&self) -> M { + self.mant + } + + /// Get the exponent component. + #[inline(always)] + pub fn exponent(&self) -> i32 { + self.exp + } +} diff --git a/rust/vendor/lexical-util/src/f16.rs b/rust/vendor/lexical-util/src/f16.rs new file mode 100644 index 0000000..6f6efd2 --- /dev/null +++ b/rust/vendor/lexical-util/src/f16.rs @@ -0,0 +1,9 @@ +//! Half-precision IEEE-754 floating point implementation. +//! +//! f16 is meant as an interchange format, and therefore there may be +//! rounding error in using it for fast-path algorithms. Since there +//! are no native operations using `f16`, this is of minimal concern. + +#![cfg(feature = "f16")] + +pub use float16::f16; diff --git a/rust/vendor/lexical-util/src/feature_format.rs b/rust/vendor/lexical-util/src/feature_format.rs new file mode 100644 index 0000000..79d978b --- /dev/null +++ b/rust/vendor/lexical-util/src/feature_format.rs @@ -0,0 +1,1453 @@ +//! Configuration options for parsing and formatting numbers. +//! +//! This comprises 2 parts: a low-level API for generating packed structs +//! containing enumerating for number formats (both syntax and lexer). +//! +//! # Syntax Format +//! +//! The syntax format defines **which** numeric string are valid. +//! For example, if exponent notation is required or not +//! allowed. +//! +//! # Control Format +//! +//! The control format defines what characters are valid, that is, which +//! characters should be consider valid to continue tokenization. + +#![cfg(feature = "format")] + +use crate::error::Error; +use crate::format_builder::NumberFormatBuilder; +use crate::format_flags as flags; + +/// Add multiple flags to `SyntaxFormat`. +macro_rules! from_flag { + ($format:ident, $flag:ident) => {{ + $format & flags::$flag != 0 + }}; +} + +/// Helper to access features from the packed format struct. +/// +/// This contains accessory methods to read the formatting settings +/// without using bitmasks directly on the underlying packed struct. +/// +/// Some of the core functionality includes support for: +/// - Digit separators: ignored characters used to make numbers more readable, +/// such as `100,000`. +/// - Non-decimal radixes: writing or parsing numbers written in binary, +/// hexadecimal, or other bases. +/// - Special numbers: disabling support for special floating-point, such as +/// [`NaN`][f64::NAN]. +/// - Number components: require signs, significant digits, and more. +/// +/// This should always be constructed via [`NumberFormatBuilder`]. +/// See [`NumberFormatBuilder`] for the fields for the packed struct. +/// +/// # Examples +/// +/// ```rust +/// # #[cfg(feature = "format")] { +/// use lexical_util::format::{RUST_LITERAL, NumberFormat}; +/// +/// let format = NumberFormat::<{ RUST_LITERAL }> {}; +/// assert!(format.no_positive_mantissa_sign()); +/// assert!(format.no_special()); +/// assert!(format.internal_digit_separator()); +/// assert!(format.trailing_digit_separator()); +/// assert!(format.consecutive_digit_separator()); +/// assert!(!format.no_exponent_notation()); +/// # } +/// ``` +pub struct NumberFormat; + +#[rustfmt::skip] +impl NumberFormat { + // CONSTRUCTORS + + /// Create new instance (for methods and validation). + /// + /// This uses the same settings as in the `FORMAT` packed struct. + #[inline(always)] + pub const fn new() -> Self { + Self {} + } + + // VALIDATION + + /// Determine if the number format is valid. + #[inline(always)] + pub const fn is_valid(&self) -> bool { + self.error().is_success() + } + + /// Get the error type from the format. + /// + /// If [`Error::Success`] is returned, then no error occurred. + #[inline(always)] + pub const fn error(&self) -> Error { + format_error_impl(FORMAT) + } + + /// Determine if the radixes in the number format are valid. + #[inline(always)] + pub const fn is_valid_radix(&self) -> bool { + self.error_radix().is_success() + } + + /// Get the error type from the radix-only for the format. + /// + /// If [`Error::Success`] is returned, then no error occurred. + #[inline(always)] + pub const fn error_radix(&self) -> Error { + radix_error_impl(FORMAT) + } + + // NON-DIGIT SEPARATOR FLAGS & MASKS + + /// If digits are required before the decimal point. + /// + /// See [`required_integer_digits`][Self::required_integer_digits]. + pub const REQUIRED_INTEGER_DIGITS: bool = from_flag!(FORMAT, REQUIRED_INTEGER_DIGITS); + + /// Get if digits are required before the decimal point. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `0.1` | ✔️ | + /// | `1` | ✔️ | + /// | `.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn required_integer_digits(&self) -> bool { + Self::REQUIRED_INTEGER_DIGITS + } + + /// If digits are required after the decimal point. + /// + /// See [`required_fraction_digits`][Self::required_fraction_digits]. + pub const REQUIRED_FRACTION_DIGITS: bool = from_flag!(FORMAT, REQUIRED_FRACTION_DIGITS); + + /// Get if digits are required after the decimal point. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1` | ✔️ | + /// | `1.` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn required_fraction_digits(&self) -> bool { + Self::REQUIRED_FRACTION_DIGITS + } + + /// If digits are required after the exponent character. + /// + /// See [`required_exponent_digits`][Self::required_exponent_digits]. + pub const REQUIRED_EXPONENT_DIGITS: bool = from_flag!(FORMAT, REQUIRED_EXPONENT_DIGITS); + + /// Get if digits are required after the exponent character. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e+3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// | `1.1e+` | ❌ | + /// | `1.1e` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn required_exponent_digits(&self) -> bool { + Self::REQUIRED_EXPONENT_DIGITS + } + + /// If significant digits are required. + /// + /// See [`required_mantissa_digits`][Self::required_mantissa_digits]. + pub const REQUIRED_MANTISSA_DIGITS: bool = from_flag!(FORMAT, REQUIRED_MANTISSA_DIGITS); + + /// Get if at least 1 significant digit is required. + /// + /// If not required, then values like `.` (`0`) are valid, but empty strings + /// are still invalid. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `.` | ✔️ | + /// | `e10` | ✔️ | + /// | `.e10` | ✔️ | + /// | `` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn required_mantissa_digits(&self) -> bool { + Self::REQUIRED_MANTISSA_DIGITS + } + + /// If at least 1 digit in the number is required. + /// + /// See [`required_digits`][Self::required_digits]. + pub const REQUIRED_DIGITS: bool = from_flag!(FORMAT, REQUIRED_DIGITS); + + /// Get if at least 1 digit in the number is required. + /// + /// This requires either [`mantissa`] or [`exponent`] digits. + /// + /// [`mantissa`]: Self::required_mantissa_digits + /// [`exponent`]: Self::required_exponent_digits + #[inline(always)] + pub const fn required_digits(&self) -> bool { + Self::REQUIRED_DIGITS + } + + /// If a positive sign before the mantissa is not allowed. + /// + /// See [`no_positive_mantissa_sign`][Self::no_positive_mantissa_sign]. + pub const NO_POSITIVE_MANTISSA_SIGN: bool = from_flag!(FORMAT, NO_POSITIVE_MANTISSA_SIGN); + + /// Get if a positive sign before the mantissa is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + #[inline(always)] + pub const fn no_positive_mantissa_sign(&self) -> bool { + Self::NO_POSITIVE_MANTISSA_SIGN + } + + /// If a sign symbol before the mantissa is required. + /// + /// See [`required_mantissa_sign`][Self::required_mantissa_sign]. + pub const REQUIRED_MANTISSA_SIGN: bool = from_flag!(FORMAT, REQUIRED_MANTISSA_SIGN); + + /// Get if a sign symbol before the mantissa is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ❌ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + #[inline(always)] + pub const fn required_mantissa_sign(&self) -> bool { + Self::REQUIRED_MANTISSA_SIGN + } + + /// If exponent notation is not allowed. + /// + /// See [`no_exponent_notation`][Self::no_exponent_notation]. + pub const NO_EXPONENT_NOTATION: bool = from_flag!(FORMAT, NO_EXPONENT_NOTATION); + + /// Get if exponent notation is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1.1` | ✔️ | + /// | `1.1e` | ❌ | + /// | `1.1e5` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn no_exponent_notation(&self) -> bool { + Self::NO_EXPONENT_NOTATION + } + + /// If a positive sign before the exponent is not allowed. + /// + /// See [`no_positive_exponent_sign`][Self::no_positive_exponent_sign]. + pub const NO_POSITIVE_EXPONENT_SIGN: bool = from_flag!(FORMAT, NO_POSITIVE_EXPONENT_SIGN); + + /// Get if a positive sign before the exponent is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ✔️ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn no_positive_exponent_sign(&self) -> bool { + Self::NO_POSITIVE_EXPONENT_SIGN + } + + /// If a sign symbol before the exponent is required. + /// + /// See [`required_exponent_sign`][Self::required_exponent_sign]. + pub const REQUIRED_EXPONENT_SIGN: bool = from_flag!(FORMAT, REQUIRED_EXPONENT_SIGN); + + /// Get if a sign symbol before the exponent is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ❌ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn required_exponent_sign(&self) -> bool { + Self::REQUIRED_EXPONENT_SIGN + } + + /// If an exponent without fraction is not allowed. + /// + /// See [`no_exponent_without_fraction`][Self::no_exponent_without_fraction]. + pub const NO_EXPONENT_WITHOUT_FRACTION: bool = from_flag!(FORMAT, NO_EXPONENT_WITHOUT_FRACTION); + + /// Get if an exponent without fraction is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1e3` | ❌ | + /// | `1.e3` | ❌ | + /// | `1.1e` | ✔️ | + /// | `.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn no_exponent_without_fraction(&self) -> bool { + Self::NO_EXPONENT_WITHOUT_FRACTION + } + + /// If special (non-finite) values are not allowed. + /// + /// See [`no_special`][Self::no_special]. + pub const NO_SPECIAL: bool = from_flag!(FORMAT, NO_SPECIAL); + + /// Get if special (non-finite) values are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `NaN` | ❌ | + /// | `inf` | ❌ | + /// | `-Infinity` | ❌ | + /// | `1.1e` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn no_special(&self) -> bool { + Self::NO_SPECIAL + } + + /// If special (non-finite) values are case-sensitive. + /// + /// See [`case_sensitive_special`][Self::case_sensitive_special]. + pub const CASE_SENSITIVE_SPECIAL: bool = from_flag!(FORMAT, CASE_SENSITIVE_SPECIAL); + + /// Get if special (non-finite) values are case-sensitive. + /// + /// If set to [`true`], then `NaN` and `nan` are treated as the same value + /// ([Not a Number][f64::NAN]). Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn case_sensitive_special(&self) -> bool { + Self::CASE_SENSITIVE_SPECIAL + } + + /// If leading zeros before an integer are not allowed. + /// + /// See [`no_integer_leading_zeros`][Self::no_integer_leading_zeros]. + pub const NO_INTEGER_LEADING_ZEROS: bool = from_flag!(FORMAT, NO_INTEGER_LEADING_ZEROS); + + /// Get if leading zeros before an integer are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// + /// # Used For + /// + /// - Parse Integer + #[inline(always)] + pub const fn no_integer_leading_zeros(&self) -> bool { + Self::NO_INTEGER_LEADING_ZEROS + } + + /// If leading zeros before a float are not allowed. + /// + /// See [`no_float_leading_zeros`][Self::no_float_leading_zeros]. + pub const NO_FLOAT_LEADING_ZEROS: bool = from_flag!(FORMAT, NO_FLOAT_LEADING_ZEROS); + + /// Get if leading zeros before a float are not allowed. + /// + /// This is before the significant digits of the float, that is, if there is + /// 1 or more digits in the integral component and the leading digit is 0, + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `01.0` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// | `0.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn no_float_leading_zeros(&self) -> bool { + Self::NO_FLOAT_LEADING_ZEROS + } + + /// If exponent notation is required. + /// + /// See [`required_exponent_notation`][Self::required_exponent_notation]. + pub const REQUIRED_EXPONENT_NOTATION: bool = from_flag!(FORMAT, REQUIRED_EXPONENT_NOTATION); + + /// Get if exponent notation is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ❌ | + /// | `1.0` | ❌ | + /// | `1e3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn required_exponent_notation(&self) -> bool { + Self::REQUIRED_EXPONENT_NOTATION + } + + /// If exponent characters are case-sensitive. + /// + /// See [`case_sensitive_exponent`][Self::case_sensitive_exponent]. + pub const CASE_SENSITIVE_EXPONENT: bool = from_flag!(FORMAT, CASE_SENSITIVE_EXPONENT); + + /// Get if exponent characters are case-sensitive. + /// + /// If set to [`true`], then the exponent character `e` would be considered + /// the different from `E`. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn case_sensitive_exponent(&self) -> bool { + Self::CASE_SENSITIVE_EXPONENT + } + + /// If base prefixes are case-sensitive. + /// + /// See [`case_sensitive_base_prefix`][Self::case_sensitive_base_prefix]. + pub const CASE_SENSITIVE_BASE_PREFIX: bool = from_flag!(FORMAT, CASE_SENSITIVE_BASE_PREFIX); + + /// Get if base prefixes are case-sensitive. + /// + /// If set to [`true`], then the base prefix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn case_sensitive_base_prefix(&self) -> bool { + Self::CASE_SENSITIVE_BASE_PREFIX + } + + /// If base suffixes are case-sensitive. + /// + /// See [`case_sensitive_base_suffix`][Self::case_sensitive_base_suffix]. + pub const CASE_SENSITIVE_BASE_SUFFIX: bool = from_flag!(FORMAT, CASE_SENSITIVE_BASE_SUFFIX); + + /// Get if base suffixes are case-sensitive. + /// + /// If set to [`true`], then the base suffix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn case_sensitive_base_suffix(&self) -> bool { + Self::CASE_SENSITIVE_BASE_SUFFIX + } + + // DIGIT SEPARATOR FLAGS & MASKS + + /// If digit separators are allowed between integer digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. + /// + /// See [`integer_internal_digit_separator`][Self::integer_internal_digit_separator]. + pub const INTEGER_INTERNAL_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, INTEGER_INTERNAL_DIGIT_SEPARATOR); + + /// Get if digit separators are allowed between integer digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ✔️ | + /// | `1_` | ❌ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn integer_internal_digit_separator(&self) -> bool { + Self::INTEGER_INTERNAL_DIGIT_SEPARATOR + } + + /// If digit separators are allowed between fraction digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. + /// + /// See [`fraction_internal_digit_separator`][Self::fraction_internal_digit_separator]. + pub const FRACTION_INTERNAL_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, FRACTION_INTERNAL_DIGIT_SEPARATOR); + + /// Get if digit separators are allowed between fraction digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ✔️ | + /// | `1.1_` | ❌ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn fraction_internal_digit_separator(&self) -> bool { + Self::FRACTION_INTERNAL_DIGIT_SEPARATOR + } + + /// If digit separators are allowed between exponent digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. + /// + /// See [`exponent_internal_digit_separator`][Self::exponent_internal_digit_separator]. + pub const EXPONENT_INTERNAL_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, EXPONENT_INTERNAL_DIGIT_SEPARATOR); + + /// Get if digit separators are allowed between exponent digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ✔️ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn exponent_internal_digit_separator(&self) -> bool { + Self::EXPONENT_INTERNAL_DIGIT_SEPARATOR + } + + /// If digit separators are allowed between digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. + /// + /// See [`internal_digit_separator`][Self::internal_digit_separator]. + pub const INTERNAL_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, INTERNAL_DIGIT_SEPARATOR); + + /// Get if digit separators are allowed between digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. This is equivalent to any of [`integer_internal_digit_separator`], + /// [`fraction_internal_digit_separator`], or + /// [`exponent_internal_digit_separator`] being set. + /// + /// [`integer_internal_digit_separator`]: Self::integer_internal_digit_separator + /// [`fraction_internal_digit_separator`]: Self::fraction_internal_digit_separator + /// [`exponent_internal_digit_separator`]: Self::exponent_internal_digit_separator + #[inline(always)] + pub const fn internal_digit_separator(&self) -> bool { + Self::INTERNAL_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed before any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`integer_leading_digit_separator`][Self::integer_leading_digit_separator]. + pub const INTEGER_LEADING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, INTEGER_LEADING_DIGIT_SEPARATOR); + + /// Get if a digit separator is allowed before any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ❌ | + /// | `_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn integer_leading_digit_separator(&self) -> bool { + Self::INTEGER_LEADING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed before any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`fraction_leading_digit_separator`][Self::fraction_leading_digit_separator]. + pub const FRACTION_LEADING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, FRACTION_LEADING_DIGIT_SEPARATOR); + + /// Get if a digit separator is allowed before any fraction digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ❌ | + /// | `1._1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn fraction_leading_digit_separator(&self) -> bool { + Self::FRACTION_LEADING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed before any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`exponent_leading_digit_separator`][Self::exponent_leading_digit_separator]. + pub const EXPONENT_LEADING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, EXPONENT_LEADING_DIGIT_SEPARATOR); + + /// Get if a digit separator is allowed before any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn exponent_leading_digit_separator(&self) -> bool { + Self::EXPONENT_LEADING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed before any digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`leading_digit_separator`][Self::leading_digit_separator]. + pub const LEADING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, LEADING_DIGIT_SEPARATOR); + + /// Get if a digit separator is allowed before any digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. This is equivalent to + /// any of [`integer_leading_digit_separator`], + /// [`fraction_leading_digit_separator`], or + /// [`exponent_leading_digit_separator`] being set. + /// + /// [`integer_leading_digit_separator`]: Self::integer_leading_digit_separator + /// [`fraction_leading_digit_separator`]: Self::fraction_leading_digit_separator + /// [`exponent_leading_digit_separator`]: Self::exponent_leading_digit_separator + #[inline(always)] + pub const fn leading_digit_separator(&self) -> bool { + Self::LEADING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed after any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`integer_trailing_digit_separator`][Self::integer_trailing_digit_separator]. + pub const INTEGER_TRAILING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, INTEGER_TRAILING_DIGIT_SEPARATOR); + + /// Get if a digit separator is allowed after any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ✔️ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn integer_trailing_digit_separator(&self) -> bool { + Self::INTEGER_TRAILING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed after any fraction digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`fraction_trailing_digit_separator`][Self::fraction_trailing_digit_separator]. + pub const FRACTION_TRAILING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, FRACTION_TRAILING_DIGIT_SEPARATOR); + + /// Get if a digit separator is allowed after any fraction digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ✔️ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn fraction_trailing_digit_separator(&self) -> bool { + Self::FRACTION_TRAILING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed after any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`exponent_trailing_digit_separator`][Self::exponent_trailing_digit_separator]. + pub const EXPONENT_TRAILING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, EXPONENT_TRAILING_DIGIT_SEPARATOR); + + /// Get if a digit separator is allowed after any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ✔️ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn exponent_trailing_digit_separator(&self) -> bool { + Self::EXPONENT_TRAILING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed after any digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`trailing_digit_separator`][Self::trailing_digit_separator]. + pub const TRAILING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, TRAILING_DIGIT_SEPARATOR); + + /// Get if a digit separator is allowed after any digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. This is equivalent to + /// any of [`integer_trailing_digit_separator`], + /// [`fraction_trailing_digit_separator`], or + /// [`exponent_trailing_digit_separator`] being set. + /// + /// [`integer_trailing_digit_separator`]: Self::integer_trailing_digit_separator + /// [`fraction_trailing_digit_separator`]: Self::fraction_trailing_digit_separator + /// [`exponent_trailing_digit_separator`]: Self::exponent_trailing_digit_separator + #[inline(always)] + pub const fn trailing_digit_separator(&self) -> bool { + Self::TRAILING_DIGIT_SEPARATOR + } + + /// If multiple consecutive integer digit separators are allowed. + /// + /// See [`integer_consecutive_digit_separator`][Self::integer_consecutive_digit_separator]. + pub const INTEGER_CONSECUTIVE_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, INTEGER_CONSECUTIVE_DIGIT_SEPARATOR); + + /// Get if multiple consecutive integer digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// integer. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn integer_consecutive_digit_separator(&self) -> bool { + Self::INTEGER_CONSECUTIVE_DIGIT_SEPARATOR + } + + /// If multiple consecutive fraction digit separators are allowed. + /// + /// See [`fraction_consecutive_digit_separator`][Self::fraction_consecutive_digit_separator]. + pub const FRACTION_CONSECUTIVE_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, FRACTION_CONSECUTIVE_DIGIT_SEPARATOR); + + /// Get if multiple consecutive fraction digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// fraction. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn fraction_consecutive_digit_separator(&self) -> bool { + Self::FRACTION_CONSECUTIVE_DIGIT_SEPARATOR + } + + /// If multiple consecutive exponent digit separators are allowed. + /// + /// See [`exponent_consecutive_digit_separator`][Self::exponent_consecutive_digit_separator]. + pub const EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR); + + /// Get if multiple consecutive exponent digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// exponent. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn exponent_consecutive_digit_separator(&self) -> bool { + Self::EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR + } + + /// If multiple consecutive digit separators are allowed. + /// + /// See [`consecutive_digit_separator`][Self::consecutive_digit_separator]. + pub const CONSECUTIVE_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, CONSECUTIVE_DIGIT_SEPARATOR); + + /// Get if multiple consecutive digit separators are allowed. + /// + /// This is equivalent to any of [`integer_consecutive_digit_separator`], + /// [`fraction_consecutive_digit_separator`], or + /// [`exponent_consecutive_digit_separator`] being set. + /// + /// [`integer_consecutive_digit_separator`]: Self::integer_consecutive_digit_separator + /// [`fraction_consecutive_digit_separator`]: Self::fraction_consecutive_digit_separator + /// [`exponent_consecutive_digit_separator`]: Self::exponent_consecutive_digit_separator + #[inline(always)] + pub const fn consecutive_digit_separator(&self) -> bool { + Self::CONSECUTIVE_DIGIT_SEPARATOR + } + + /// If any digit separators are allowed in special (non-finite) values. + /// + /// See [`special_digit_separator`][Self::special_digit_separator]. + pub const SPECIAL_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, SPECIAL_DIGIT_SEPARATOR); + + /// Get if any digit separators are allowed in special (non-finite) values. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for any special floats: for example, `N__a_N_` is considered + /// the same as `NaN`. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn special_digit_separator(&self) -> bool { + Self::SPECIAL_DIGIT_SEPARATOR + } + + // CHARACTERS + + /// The digit separator character in the packed struct. + /// + /// See [`digit_separator`][Self::digit_separator]. + pub const DIGIT_SEPARATOR: u8 = flags::digit_separator(FORMAT); + + /// Get the digit separator for the number format. + /// + /// Digit separators are frequently used in number literals to group + /// digits: `1,000,000` is a lot more readable than `1000000`, but + /// the `,` characters should be ignored in the parsing of the number. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `0`, or no digit separators allowed. + /// + /// # Examples + /// + /// Using a digit separator of `_` (note that the validity + /// oh where a digit separator can appear depends on the other digit + /// separator flags). + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1_4` | ✔️ | + /// | `+_14` | ✔️ | + /// | `+14e3_5` | ✔️ | + /// | `1_d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn digit_separator(&self) -> u8 { + Self::DIGIT_SEPARATOR + } + + /// Get if the format has a digit separator. + #[inline(always)] + pub const fn has_digit_separator(&self) -> bool { + self.digit_separator() != 0 + } + + /// The base prefix character in the packed struct. + /// + /// See [`base_prefix`][Self::base_prefix]. + pub const BASE_PREFIX: u8 = flags::base_prefix(FORMAT); + + /// Get the optional character for the base prefix. + /// + /// This character will come after a leading zero, so for example + /// setting the base prefix to `x` means that a leading `0x` will + /// be ignore, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to `0`, or no base prefix allowed. + /// + /// # Examples + /// + /// Using a base prefix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `0x1` | ✔️ | + /// | `x1` | ❌ | + /// | `1` | ✔️ | + /// | `1x` | ❌ | + /// | `1x1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn base_prefix(&self) -> u8 { + Self::BASE_PREFIX + } + + /// Get if the format has a base suffix. + #[inline(always)] + pub const fn has_base_prefix(&self) -> bool { + self.base_prefix() != 0 + } + + /// The base suffix character in the packed struct. + /// + /// See [`base_suffix`][Self::base_suffix]. + pub const BASE_SUFFIX: u8 = flags::base_suffix(FORMAT); + + /// Get the optional character for the base suffix. + /// + /// This character will at the end of the buffer, so for example + /// setting the base prefix to `x` means that a trailing `x` will + /// be ignored, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to `0`, or no base suffix allowed. + /// + /// # Examples + /// + /// Using a base suffix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1x` | ✔️ | + /// | `1d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn base_suffix(&self) -> u8 { + Self::BASE_SUFFIX + } + + /// Get if the format has a base suffix. + #[inline(always)] + pub const fn has_base_suffix(&self) -> bool { + self.base_suffix() != 0 + } + + // RADIX + + /// The radix for the significant digits in the packed struct. + /// + /// See [`mantissa_radix`][Self::mantissa_radix]. + pub const MANTISSA_RADIX: u32 = flags::mantissa_radix(FORMAT); + + /// Get the radix for mantissa digits. + /// + /// This is only used for the significant digits, that is, the integral and + /// fractional components. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults + /// to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "10011010010" | 1234 | + /// | 3 | "1200201" | 1234 | + /// | 8 | "2322" | 1234 | + /// | 10 | "1234" | 1234 | + /// | 16 | "4d2" | 1234 | + /// | 31 | "18p" | 1234 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// - Write Integer + #[inline(always)] + pub const fn mantissa_radix(&self) -> u32 { + Self::MANTISSA_RADIX + } + + /// The radix for the significant digits in the packed struct. + /// + /// Alias for [`MANTISSA_RADIX`][Self::MANTISSA_RADIX]. + pub const RADIX: u32 = Self::MANTISSA_RADIX; + + /// Get the radix for the significant digits. + /// + /// This is an alias for [`mantissa_radix`][Self::mantissa_radix]. + #[inline(always)] + pub const fn radix(&self) -> u32 { + Self::RADIX + } + + /// Get the `radix^2` for the significant digits. + #[inline(always)] + pub const fn radix2(&self) -> u32 { + self.radix().wrapping_mul(self.radix()) + } + + /// Get the `radix^4` for the significant digits. + #[inline(always)] + pub const fn radix4(&self) -> u32 { + self.radix2().wrapping_mul(self.radix2()) + } + + /// Get the `radix^8` for the significant digits. + #[inline(always)] + pub const fn radix8(&self) -> u32 { + // NOTE: radix >= 16 will overflow here but this has no security concerns + self.radix4().wrapping_mul(self.radix4()) + } + + /// The base for the exponent. + /// + /// See [`exponent_base`][Self::exponent_base]. + pub const EXPONENT_BASE: u32 = flags::exponent_base(FORMAT); + + /// Get the radix for the exponent. + /// + /// For example, in `1.234e3`, it means `1.234 * 10^3`, and the exponent + /// base here is 10. Some programming languages, like C, support hex floats + /// with an exponent base of 2, for example `0x1.8p3`, or `1.5 * 2^3`. + /// Defaults to `10`. Can only be modified with [`feature`][crate#features] + /// `power-of-two` or `radix`. Defaults to `10`. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn exponent_base(&self) -> u32 { + Self::EXPONENT_BASE + } + + /// The radix for the exponent digits. + /// + /// See [`exponent_radix`][Self::exponent_radix]. + pub const EXPONENT_RADIX: u32 = flags::exponent_radix(FORMAT); + + /// Get the radix for exponent digits. + /// + /// This is only used for the exponent digits. We assume the radix for the + /// significant digits ([`mantissa_radix`][Self::mantissa_radix]) is + /// 10 as is the exponent base. Defaults to `10`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "1.234^1100" | 1.234e9 | + /// | 3 | "1.234^110" | 1.234e9 | + /// | 8 | "1.234^14" | 1.234e9 | + /// | 10 | "1.234^12" | 1.234e9 | + /// | 16 | "1.234^c" | 1.234e9 | + /// | 31 | "1.234^c" | 1.234e9 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn exponent_radix(&self) -> u32 { + Self::EXPONENT_RADIX + } + + // FLAGS + + /// Get the flags from the number format. + /// + /// This contains all the non-character and non-radix values + /// in the packed struct. + #[inline(always)] + pub const fn flags(&self) -> u128 { + FORMAT & flags::FLAG_MASK + } + + /// Get the interface flags from the number format. + /// + /// This contains all the flags that dictate code flows, and + /// therefore excludes logic like case-sensitive characters. + #[inline(always)] + pub const fn interface_flags(&self) -> u128 { + FORMAT & flags::INTERFACE_FLAG_MASK + } + + /// Get the digit separator flags from the number format. + #[inline(always)] + pub const fn digit_separator_flags(&self) -> u128 { + FORMAT & flags::DIGIT_SEPARATOR_FLAG_MASK + } + + /// Get the exponent flags from the number format. + /// + /// This contains all the flags pertaining to exponent + /// formats, including digit separators. + #[inline(always)] + pub const fn exponent_flags(&self) -> u128 { + FORMAT & flags::EXPONENT_FLAG_MASK + } + + /// Get the integer digit separator flags from the number format. + #[inline(always)] + pub const fn integer_digit_separator_flags(&self) -> u128 { + FORMAT & flags::INTEGER_DIGIT_SEPARATOR_FLAG_MASK + } + + /// Get the fraction digit separator flags from the number format. + #[inline(always)] + pub const fn fraction_digit_separator_flags(&self) -> u128 { + FORMAT & flags::FRACTION_DIGIT_SEPARATOR_FLAG_MASK + } + + /// Get the exponent digit separator flags from the number format. + #[inline(always)] + pub const fn exponent_digit_separator_flags(&self) -> u128 { + FORMAT & flags::EXPONENT_DIGIT_SEPARATOR_FLAG_MASK + } + + // BUILDER + + /// Get [`NumberFormatBuilder`] as a static function. + #[inline(always)] + pub const fn builder() -> NumberFormatBuilder { + NumberFormatBuilder::new() + } + + /// Create [`NumberFormatBuilder`] using existing values. + #[inline(always)] + pub const fn rebuild() -> NumberFormatBuilder { + NumberFormatBuilder::rebuild(FORMAT) + } +} + +impl Default for NumberFormat { + fn default() -> Self { + Self::new() + } +} + +/// Get if the radix is valid. +#[inline(always)] +pub(crate) const fn radix_error_impl(format: u128) -> Error { + if !flags::is_valid_radix(flags::mantissa_radix(format)) { + Error::InvalidMantissaRadix + } else if !flags::is_valid_radix(flags::exponent_base(format)) { + Error::InvalidExponentBase + } else if !flags::is_valid_radix(flags::exponent_radix(format)) { + Error::InvalidExponentRadix + } else { + Error::Success + } +} + +/// Get the error type from the format. +#[inline(always)] +#[allow(clippy::if_same_then_else)] // reason="all are different logic conditions" +pub(crate) const fn format_error_impl(format: u128) -> Error { + if !flags::is_valid_radix(flags::mantissa_radix(format)) { + Error::InvalidMantissaRadix + } else if !flags::is_valid_radix(flags::exponent_base(format)) { + Error::InvalidExponentBase + } else if !flags::is_valid_radix(flags::exponent_radix(format)) { + Error::InvalidExponentRadix + } else if !flags::is_valid_digit_separator(format) { + Error::InvalidDigitSeparator + } else if !flags::is_valid_base_prefix(format) { + Error::InvalidBasePrefix + } else if !flags::is_valid_base_suffix(format) { + Error::InvalidBaseSuffix + } else if !flags::is_valid_punctuation(format) { + Error::InvalidPunctuation + } else if !flags::is_valid_exponent_flags(format) { + Error::InvalidExponentFlags + } else if from_flag!(format, NO_POSITIVE_MANTISSA_SIGN) + && from_flag!(format, REQUIRED_MANTISSA_SIGN) + { + Error::InvalidMantissaSign + } else if from_flag!(format, NO_POSITIVE_EXPONENT_SIGN) + && from_flag!(format, REQUIRED_EXPONENT_SIGN) + { + Error::InvalidExponentSign + } else if from_flag!(format, NO_SPECIAL) && from_flag!(format, CASE_SENSITIVE_SPECIAL) { + Error::InvalidSpecial + } else if from_flag!(format, NO_SPECIAL) && from_flag!(format, SPECIAL_DIGIT_SEPARATOR) { + Error::InvalidSpecial + } else if (format & flags::INTEGER_DIGIT_SEPARATOR_FLAG_MASK) + == flags::INTEGER_CONSECUTIVE_DIGIT_SEPARATOR + { + Error::InvalidConsecutiveIntegerDigitSeparator + } else if (format & flags::FRACTION_DIGIT_SEPARATOR_FLAG_MASK) + == flags::FRACTION_CONSECUTIVE_DIGIT_SEPARATOR + { + Error::InvalidConsecutiveFractionDigitSeparator + } else if (format & flags::EXPONENT_DIGIT_SEPARATOR_FLAG_MASK) + == flags::EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR + { + Error::InvalidConsecutiveExponentDigitSeparator + } else { + Error::Success + } +} diff --git a/rust/vendor/lexical-util/src/format.rs b/rust/vendor/lexical-util/src/format.rs new file mode 100644 index 0000000..a3646b4 --- /dev/null +++ b/rust/vendor/lexical-util/src/format.rs @@ -0,0 +1,657 @@ +//! The creation and processing of number format packed structs. +//! +//! This creates the format specification as a 128-bit packed struct, +//! represented as a [`u128`] through the [`NumberFormatBuilder`] and +//! with helpers to access format options through [`NumberFormat`]. +//! +//! This has a consistent API whether or not the [`format`][crate#features] +//! feature is enabled, however, most functionality will be disabled if the +//! feature is not enabled. +//! +//! # Creating Formats +//! +//! Formats can be created through [`NumberFormatBuilder`]: +//! +//! ```rust +//! # #[cfg(feature = "format")] { +//! use core::num; +//! +//! use lexical_util::{NumberFormat, NumberFormatBuilder}; +//! +//! // create the format for literal Rustt floats +//! const RUST: u128 = NumberFormatBuilder::new() +//! .digit_separator(num::NonZeroU8::new(b'_')) +//! .required_digits(true) +//! .no_positive_mantissa_sign(true) +//! .no_special(true) +//! .internal_digit_separator(true) +//! .trailing_digit_separator(true) +//! .consecutive_digit_separator(true) +//! .build_strict(); +//! +//! // then, access the formats's properties +//! let format = NumberFormat::<{ RUST }> {}; +//! assert!(format.no_positive_mantissa_sign()); +//! assert!(format.no_special()); +//! assert!(format.internal_digit_separator()); +//! assert!(format.trailing_digit_separator()); +//! assert!(format.consecutive_digit_separator()); +//! assert!(!format.no_exponent_notation()); +//! # } +//! ``` +//! +//! These pre-built formats can then be used for [`FromLexicalWithOptions`] +//! and [`ToLexicalWithOptions`] conversions. +//! +//! # Pre-Defined Formats +//! +//! These are the pre-defined formats for parsing numbers from various +//! programming, markup, and data languages. +//! +//! - [`STANDARD`]: Standard number format. This is identical to the Rust string +//! format. +#![cfg_attr( + feature = "format", + doc = " +- [`RUST_LITERAL`]: Number format for a [`Rust`] literal floating-point number. +- [`RUST_STRING`]: Number format to parse a [`Rust`] float from string. +- [`PYTHON_LITERAL`]: Number format for a [`Python`] literal floating-point number. +- [`PYTHON_STRING`]: Number format to parse a [`Python`] float from string. +- [`PYTHON3_LITERAL`]: Number format for a [`Python3`] literal floating-point number. +- [`PYTHON3_STRING`]: Number format to parse a [`Python3`] float from string. +- [`PYTHON36_LITERAL`]: Number format for a [`Python3.6`] or higher literal floating-point number. +- [`PYTHON35_LITERAL`]: Number format for a [`Python3.5`] or lower literal floating-point number. +- [`PYTHON2_LITERAL`]: Number format for a [`Python2`] literal floating-point number. +- [`PYTHON2_STRING`]: Number format to parse a [`Python2`] float from string. +- [`CXX_LITERAL`]: Number format for a [`C++`] literal floating-point number. +- [`CXX_STRING`]: Number format to parse a [`C++`] float from string. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`CXX_HEX_LITERAL`]: Number format for a [`C++`] literal hexadecimal floating-point number. +- [`CXX_HEX_STRING`]: Number format to parse a [`C++`] hexadecimal float from string. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CXX20_LITERAL`]: Number format for a [`C++20`] literal floating-point number. +- [`CXX20_STRING`]: Number format for a [`C++20`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`CXX20_HEX_LITERAL`]: Number format for a [`C++20`] literal hexadecimal floating-point number. +- [`CXX20_HEX_STRING`]: Number format for a [`C++20`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CXX17_LITERAL`]: Number format for a [`C++17`] literal floating-point number. +- [`CXX17_STRING`]: Number format for a [`C++17`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`CXX17_HEX_LITERAL`]: Number format for a [`C++17`] literal hexadecimal floating-point number. +- [`CXX17_HEX_STRING`]: Number format for a [`C++17`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CXX14_LITERAL`]: Number format for a [`C++14`] literal floating-point number. +- [`CXX14_STRING`]: Number format for a [`C++14`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`CXX14_HEX_STRING`]: Number format for a [`C++14`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CXX11_LITERAL`]: Number format for a [`C++11`] literal floating-point number. +- [`CXX11_STRING`]: Number format for a [`C++11`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`CXX11_HEX_STRING`]: Number format for a [`C++11`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CXX03_LITERAL`]: Number format for a [`C++03`] literal floating-point number. +- [`CXX03_STRING`]: Number format for a [`C++03`] string floating-point number. +- [`CXX98_LITERAL`]: Number format for a [`C++98`] literal floating-point number. +- [`CXX98_STRING`]: Number format for a [`C++98`] string floating-point number. +- [`C_LITERAL`]: Number format for a [`C`] literal floating-point number. +- [`C_STRING`]: Number format for a [`C`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C_HEX_LITERAL`]: Number format for a [`C`] literal hexadecimal floating-point number. +- [`C_HEX_STRING`]: Number format for a [`C`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`C18_LITERAL`]: Number format for a [`C18`] literal floating-point number. +- [`C18_STRING`]: Number format for a [`C18`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C18_HEX_LITERAL`]: Number format for a [`C18`] literal hexadecimal floating-point number. +- [`C18_HEX_STRING`]: Number format for a [`C18`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`C11_LITERAL`]: Number format for a [`C11`] literal floating-point number. +- [`C11_STRING`]: Number format for a [`C11`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C11_HEX_LITERAL`]: Number format for a [`C11`] literal hexadecimal floating-point number. +- [`C11_HEX_STRING`]: Number format for a [`C11`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`C99_LITERAL`]: Number format for a [`C99`] literal floating-point number. +- [`C99_STRING`]: Number format for a [`C99`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C99_HEX_LITERAL`]: Number format for a [`C99`] literal hexadecimal floating-point number. +- [`C99_HEX_STRING`]: Number format for a [`C99`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`C90_LITERAL`]: Number format for a [`C90`] literal floating-point number. +- [`C90_STRING`]: Number format for a [`C90`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C90_HEX_STRING`]: Number format for a [`C90`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`C89_LITERAL`]: Number format for a [`C89`] literal floating-point number. +- [`C89_STRING`]: Number format for a [`C89`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C89_HEX_STRING`]: Number format for a [`C89`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`RUBY_LITERAL`]: Number format for a [`Ruby`] literal floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`RUBY_OCTAL_LITERAL`]: Number format for an octal [`Ruby`] literal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`RUBY_STRING`]: Number format to parse a [`Ruby`] float from string. +- [`SWIFT_LITERAL`]: Number format for a [`Swift`] literal floating-point number. +- [`SWIFT_STRING`]: Number format to parse a [`Swift`] float from string. +- [`GO_LITERAL`]: Number format for a [`Golang`] literal floating-point number. +- [`GO_STRING`]: Number format to parse a [`Golang`] float from string. +- [`HASKELL_LITERAL`]: Number format for a [`Haskell`] literal floating-point number. +- [`HASKELL_STRING`]: Number format to parse a [`Haskell`] float from string. +- [`JAVASCRIPT_LITERAL`]: Number format for a [`Javascript`] literal floating-point number. +- [`JAVASCRIPT_STRING`]: Number format to parse a [`Javascript`] float from string. +- [`PERL_LITERAL`]: Number format for a [`Perl`] literal floating-point number. +- [`PERL_STRING`]: Number format to parse a [`Perl`] float from string. +- [`PHP_LITERAL`]: Number format for a [`PHP`] literal floating-point number. +- [`PHP_STRING`]: Number format to parse a [`PHP`] float from string. +- [`JAVA_LITERAL`]: Number format for a [`Java`] literal floating-point number. +- [`JAVA_STRING`]: Number format to parse a [`Java`] float from string. +- [`R_LITERAL`]: Number format for an [`R`] literal floating-point number. +- [`R_STRING`]: Number format to parse an [`R`] float from string. +- [`KOTLIN_LITERAL`]: Number format for a [`Kotlin`] literal floating-point number. +- [`KOTLIN_STRING`]: Number format to parse a [`Kotlin`] float from string. +- [`JULIA_LITERAL`]: Number format for a [`Julia`] literal floating-point number. +- [`JULIA_STRING`]: Number format to parse a [`Julia`] float from string. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`JULIA_HEX_LITERAL`]: Number format for a [`Julia`] literal floating-point number. +- [`JULIA_HEX_STRING`]: Number format to parse a [`Julia`] float from string. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CSHARP_LITERAL`]: Number format for a [`C#`] literal floating-point number. +- [`CSHARP_STRING`]: Number format to parse a [`C#`] float from string. +- [`CSHARP7_LITERAL`]: Number format for a [`C#7`] literal floating-point number. +- [`CSHARP7_STRING`]: Number format to parse a [`C#7`] float from string. +- [`CSHARP6_LITERAL`]: Number format for a [`C#6`] literal floating-point number. +- [`CSHARP6_STRING`]: Number format to parse a [`C#6`] float from string. +- [`CSHARP5_LITERAL`]: Number format for a [`C#5`] literal floating-point number. +- [`CSHARP5_STRING`]: Number format to parse a [`C#5`] float from string. +- [`CSHARP4_LITERAL`]: Number format for a [`C#4`] literal floating-point number. +- [`CSHARP4_STRING`]: Number format to parse a [`C#4`] float from string. +- [`CSHARP3_LITERAL`]: Number format for a [`C#3`] literal floating-point number. +- [`CSHARP3_STRING`]: Number format to parse a [`C#3`] float from string. +- [`CSHARP2_LITERAL`]: Number format for a [`C#2`] literal floating-point number. +- [`CSHARP2_STRING`]: Number format to parse a [`C#2`] float from string. +- [`CSHARP1_LITERAL`]: Number format for a [`C#1`] literal floating-point number. +- [`CSHARP1_STRING`]: Number format to parse a [`C#1`] float from string. +- [`KAWA_LITERAL`]: Number format for a [`Kawa`] literal floating-point number. +- [`KAWA_STRING`]: Number format to parse a [`Kawa`] float from string. +- [`GAMBITC_LITERAL`]: Number format for a [`Gambit-C`] literal floating-point number. +- [`GAMBITC_STRING`]: Number format to parse a [`Gambit-C`] float from string. +- [`GUILE_LITERAL`]: Number format for a [`Guile`] literal floating-point number. +- [`GUILE_STRING`]: Number format to parse a [`Guile`] float from string. +- [`CLOJURE_LITERAL`]: Number format for a [`Clojure`] literal floating-point number. +- [`CLOJURE_STRING`]: Number format to parse a [`Clojure`] float from string. +- [`ERLANG_LITERAL`]: Number format for an [`Erlang`] literal floating-point number. +- [`ERLANG_STRING`]: Number format to parse an [`Erlang`] float from string. +- [`ELM_LITERAL`]: Number format for an [`Elm`] literal floating-point number. +- [`ELM_STRING`]: Number format to parse an [`Elm`] float from string. +- [`SCALA_LITERAL`]: Number format for a [`Scala`] literal floating-point number. +- [`SCALA_STRING`]: Number format to parse a [`Scala`] float from string. +- [`ELIXIR_LITERAL`]: Number format for an [`Elixir`] literal floating-point number. +- [`ELIXIR_STRING`]: Number format to parse an [`Elixir`] float from string. +- [`FORTRAN_LITERAL`]: Number format for a [`FORTRAN`] literal floating-point number. +- [`FORTRAN_STRING`]: Number format to parse a [`FORTRAN`] float from string. +- [`D_LITERAL`]: Number format for a [`D`] literal floating-point number. +- [`D_STRING`]: Number format to parse a [`D`] float from string. +- [`COFFEESCRIPT_LITERAL`]: Number format for a [`Coffeescript`] literal floating-point number. +- [`COFFEESCRIPT_STRING`]: Number format to parse a [`Coffeescript`] float from string. +- [`COBOL_LITERAL`]: Number format for a [`Cobol`] literal floating-point number. +- [`COBOL_STRING`]: Number format to parse a [`Cobol`] float from string. +- [`FSHARP_LITERAL`]: Number format for a [`F#`] literal floating-point number. +- [`FSHARP_STRING`]: Number format to parse a [`F#`] float from string. +- [`VB_LITERAL`]: Number format for a [`Visual Basic`] literal floating-point number. +- [`VB_STRING`]: Number format to parse a [`Visual Basic`] float from string. +- [`OCAML_LITERAL`]: Number format for an [`OCaml`] literal floating-point number. +- [`OCAML_STRING`]: Number format to parse an [`OCaml`] float from string. +- [`OBJECTIVEC_LITERAL`]: Number format for an [`Objective-C`] literal floating-point number. +- [`OBJECTIVEC_STRING`]: Number format to parse an [`Objective-C`] float from string. +- [`REASONML_LITERAL`]: Number format for a [`ReasonML`] literal floating-point number. +- [`REASONML_STRING`]: Number format to parse a [`ReasonML`] float from string. +- [`OCTAVE_LITERAL`]: Number format for an [`Octave`] literal floating-point number. +- [`OCTAVE_STRING`]: Number format to parse an [`Octave`] float from string. +- [`MATLAB_LITERAL`]: Number format for an [`Matlab`] literal floating-point number. +- [`MATLAB_STRING`]: Number format to parse an [`Matlab`] float from string. +- [`ZIG_LITERAL`]: Number format for a [`Zig`] literal floating-point number. +- [`ZIG_STRING`]: Number format to parse a [`Zig`] float from string. +- [`SAGE_LITERAL`]: Number format for a [`Sage`] literal floating-point number. +- [`SAGE_STRING`]: Number format to parse a [`Sage`] float from string. +- [`JSON`]: Number format for a [`JSON`][`JSON-REF`] literal floating-point number. +- [`TOML`]: Number format for a [`TOML`][`TOML-REF`] literal floating-point number. +- [`YAML`]: Number format for a [`YAML`][`YAML-REF`] literal floating-point number. +- [`XML`]: Number format for an [`XML`][`XML-REF`] literal floating-point number. +- [`SQLITE`]: Number format for a [`SQLite`] literal floating-point number. +- [`POSTGRESQL`]: Number format for a [`PostgreSQL`] literal floating-point number. +- [`MYSQL`]: Number format for a [`MySQL`] literal floating-point number. +- [`MONGODB`]: Number format for a [`MongoDB`] literal floating-point number. +" +)] +//! +#![cfg_attr( + any(feature = "parse-floats", feature = "parse-integers"), + doc = "[`FromLexicalWithOptions`]: crate::from_lexical_with_options" +)] +#![cfg_attr( + not(any(feature = "parse-floats", feature = "parse-integers")), + doc = "[`FromLexicalWithOptions`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/api.rs#L45" +)] +#![cfg_attr( + any(feature = "write-floats", feature = "write-integers"), + doc = "[`ToLexicalWithOptions`]: crate::to_lexical_with_options" +)] +#![cfg_attr( + not(any(feature = "write-floats", feature = "write-integers")), + doc = "[`ToLexicalWithOptions`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/api.rs#L151" +)] +//! +//! # Low-Level Schema +//! +//! This describes how to directly get and set flags from the [`NumberFormat`] +//! packed struct. It is not recommended to use these directly, but for example, +//! the following can be done: +//! +//! ```rust +//! # #[cfg(feature = "format")] { +//! use lexical_util::format; +//! +//! assert_eq!( +//! format::NumberFormatBuilder::new() +//! .required_integer_digits(true) +//! .build_strict(), +//! format::STANDARD | format::REQUIRED_INTEGER_DIGITS +//! ); +//! # } +//! ``` +//! +//! ## Syntax Flags +//! +//! Bitflags to get and set syntax flags for the format packed struct. +//! +//! - [`REQUIRED_INTEGER_DIGITS`]: If digits are required before the decimal +//! point. +//! - [`REQUIRED_FRACTION_DIGITS`]: If digits are required after the decimal +//! point. +//! - [`REQUIRED_EXPONENT_DIGITS`]: If digits are required after the exponent +//! character. +//! - [`REQUIRED_MANTISSA_DIGITS`]: If significant digits are required. +//! - [`REQUIRED_DIGITS`]: If at least 1 digit in the number is required. +//! - [`NO_POSITIVE_MANTISSA_SIGN`]: If a positive sign before the mantissa is +//! not allowed. +//! - [`REQUIRED_MANTISSA_SIGN`]: If a sign symbol before the mantissa is +//! required. +//! - [`NO_EXPONENT_NOTATION`]: If exponent notation is not allowed. +//! - [`NO_POSITIVE_EXPONENT_SIGN`]: If a positive sign before the exponent is +//! not allowed. +//! - [`REQUIRED_EXPONENT_SIGN`]: If a sign symbol before the exponent is +//! required. +//! - [`NO_EXPONENT_WITHOUT_FRACTION`]: If an exponent without fraction is not +//! allowed. +//! - [`NO_SPECIAL`]: If special (non-finite) values are not allowed. +//! - [`CASE_SENSITIVE_SPECIAL`]: If special (non-finite) values are +//! case-sensitive. +//! - [`NO_INTEGER_LEADING_ZEROS`]: If leading zeros before an integer are not +//! allowed. +//! - [`NO_FLOAT_LEADING_ZEROS`]: If leading zeros before a float are not +//! allowed. +//! - [`REQUIRED_EXPONENT_NOTATION`]: If exponent notation is required. +//! - [`CASE_SENSITIVE_EXPONENT`]: If exponent characters are case-sensitive. +//! - [`CASE_SENSITIVE_BASE_PREFIX`]: If base prefixes are case-sensitive. +//! - [`CASE_SENSITIVE_BASE_SUFFIX`]: If base suffixes are case-sensitive. +//! +//! [`REQUIRED_INTEGER_DIGITS`]: NumberFormat::REQUIRED_INTEGER_DIGITS +//! [`REQUIRED_FRACTION_DIGITS`]: NumberFormat::REQUIRED_FRACTION_DIGITS +//! [`REQUIRED_EXPONENT_DIGITS`]: NumberFormat::REQUIRED_EXPONENT_DIGITS +//! [`REQUIRED_MANTISSA_DIGITS`]: NumberFormat::REQUIRED_MANTISSA_DIGITS +//! [`REQUIRED_DIGITS`]: NumberFormat::REQUIRED_DIGITS +//! [`NO_POSITIVE_MANTISSA_SIGN`]: NumberFormat::NO_POSITIVE_MANTISSA_SIGN +//! [`REQUIRED_MANTISSA_SIGN`]: NumberFormat::REQUIRED_MANTISSA_SIGN +//! [`NO_EXPONENT_NOTATION`]: NumberFormat::NO_EXPONENT_NOTATION +//! [`NO_POSITIVE_EXPONENT_SIGN`]: NumberFormat::NO_POSITIVE_EXPONENT_SIGN +//! [`REQUIRED_EXPONENT_SIGN`]: NumberFormat::REQUIRED_EXPONENT_SIGN +//! [`NO_EXPONENT_WITHOUT_FRACTION`]: NumberFormat::NO_EXPONENT_WITHOUT_FRACTION +//! [`NO_SPECIAL`]: NumberFormat::NO_SPECIAL +//! [`CASE_SENSITIVE_SPECIAL`]: NumberFormat::CASE_SENSITIVE_SPECIAL +//! [`NO_INTEGER_LEADING_ZEROS`]: NumberFormat::NO_INTEGER_LEADING_ZEROS +//! [`NO_FLOAT_LEADING_ZEROS`]: NumberFormat::NO_FLOAT_LEADING_ZEROS +//! [`REQUIRED_EXPONENT_NOTATION`]: NumberFormat::REQUIRED_EXPONENT_NOTATION +//! [`CASE_SENSITIVE_EXPONENT`]: NumberFormat::CASE_SENSITIVE_EXPONENT +//! [`CASE_SENSITIVE_BASE_PREFIX`]: NumberFormat::CASE_SENSITIVE_BASE_PREFIX +//! [`CASE_SENSITIVE_BASE_SUFFIX`]: NumberFormat::CASE_SENSITIVE_BASE_SUFFIX +//! +//! ## Digit Separator Flags +//! +//! Bitflags to get and set digit separators flags for the format +//! packed struct. +//! +//! - [`INTEGER_INTERNAL_DIGIT_SEPARATOR`]: If digit separators are allowed +//! between integer digits. +//! - [`FRACTION_INTERNAL_DIGIT_SEPARATOR`]: If digit separators are allowed +//! between fraction digits. +//! - [`EXPONENT_INTERNAL_DIGIT_SEPARATOR`]: If digit separators are allowed +//! between exponent digits. +//! - [`INTEGER_LEADING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! before any integer digits. +//! - [`FRACTION_LEADING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! before any integer digits. +//! - [`EXPONENT_LEADING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! before any exponent digits. +//! - [`INTEGER_TRAILING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! after any integer digits. +//! - [`FRACTION_TRAILING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! after any fraction digits. +//! - [`EXPONENT_TRAILING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! after any exponent digits. +//! - [`INTEGER_CONSECUTIVE_DIGIT_SEPARATOR`]: If multiple consecutive integer +//! digit separators are allowed. +//! - [`FRACTION_CONSECUTIVE_DIGIT_SEPARATOR`]: If multiple consecutive fraction +//! digit separators are allowed. +//! - [`EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR`]: If multiple consecutive exponent +//! digit separators are allowed. +//! - [`INTERNAL_DIGIT_SEPARATOR`]: If digit separators are allowed between +//! digits. +//! - [`LEADING_DIGIT_SEPARATOR`]: Get if a digit separator is allowed before +//! any digits. +//! - [`TRAILING_DIGIT_SEPARATOR`]: If a digit separator is allowed after any +//! digits. +//! - [`CONSECUTIVE_DIGIT_SEPARATOR`]: If multiple consecutive digit separators +//! are allowed. +//! - [`SPECIAL_DIGIT_SEPARATOR`]: If any digit separators are allowed in +//! special (non-finite) values. +//! +//! [`INTEGER_INTERNAL_DIGIT_SEPARATOR`]: NumberFormat::INTEGER_INTERNAL_DIGIT_SEPARATOR +//! [`FRACTION_INTERNAL_DIGIT_SEPARATOR`]: NumberFormat::FRACTION_INTERNAL_DIGIT_SEPARATOR +//! [`EXPONENT_INTERNAL_DIGIT_SEPARATOR`]: NumberFormat::EXPONENT_INTERNAL_DIGIT_SEPARATOR +//! [`INTEGER_LEADING_DIGIT_SEPARATOR`]: NumberFormat::INTEGER_LEADING_DIGIT_SEPARATOR +//! [`FRACTION_LEADING_DIGIT_SEPARATOR`]: NumberFormat::FRACTION_LEADING_DIGIT_SEPARATOR +//! [`EXPONENT_LEADING_DIGIT_SEPARATOR`]: NumberFormat::EXPONENT_LEADING_DIGIT_SEPARATOR +//! [`INTEGER_TRAILING_DIGIT_SEPARATOR`]: NumberFormat::INTEGER_TRAILING_DIGIT_SEPARATOR +//! [`FRACTION_TRAILING_DIGIT_SEPARATOR`]: NumberFormat::FRACTION_TRAILING_DIGIT_SEPARATOR +//! [`EXPONENT_TRAILING_DIGIT_SEPARATOR`]: NumberFormat::EXPONENT_TRAILING_DIGIT_SEPARATOR +//! [`INTEGER_CONSECUTIVE_DIGIT_SEPARATOR`]: NumberFormat::INTEGER_CONSECUTIVE_DIGIT_SEPARATOR +//! [`FRACTION_CONSECUTIVE_DIGIT_SEPARATOR`]: NumberFormat::FRACTION_CONSECUTIVE_DIGIT_SEPARATOR +//! [`EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR`]: NumberFormat::EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR +//! [`INTERNAL_DIGIT_SEPARATOR`]: NumberFormat::INTERNAL_DIGIT_SEPARATOR +//! [`LEADING_DIGIT_SEPARATOR`]: NumberFormat::LEADING_DIGIT_SEPARATOR +//! [`TRAILING_DIGIT_SEPARATOR`]: NumberFormat::TRAILING_DIGIT_SEPARATOR +//! [`CONSECUTIVE_DIGIT_SEPARATOR`]: NumberFormat::CONSECUTIVE_DIGIT_SEPARATOR +//! [`SPECIAL_DIGIT_SEPARATOR`]: NumberFormat::SPECIAL_DIGIT_SEPARATOR +//! +//! ## Character Shifts and Masks +//! +//! Bitmasks and bit shifts to get and set control characters for the format +//! packed struct. +//! +//! - [`DIGIT_SEPARATOR_SHIFT`]: Shift to convert to and from a digit separator +//! as a `u8`. +//! - [`DIGIT_SEPARATOR`]: Mask to extract the digit separator character. +//! - [`BASE_PREFIX_SHIFT`]: Shift to convert to and from a base prefix as a +//! `u8`. +//! - [`BASE_PREFIX`]: Mask to extract the base prefix character. +//! - [`BASE_SUFFIX_SHIFT`]: Shift to convert to and from a base suffix as a +//! `u8`. +//! - [`BASE_SUFFIX`]: Mask to extract the base suffix character. +//! - [`MANTISSA_RADIX_SHIFT`]: Shift to convert to and from a mantissa radix as +//! a `u32`. +//! - [`MANTISSA_RADIX`]: Mask to extract the mantissa radix: the radix for the +//! significant digits. +//! - [`RADIX_SHIFT`]: Alias for [`MANTISSA_RADIX_SHIFT`]. +//! - [`RADIX`]: Alias for [`MANTISSA_RADIX`]. +//! - [`EXPONENT_BASE_SHIFT`]: Shift to convert to and from an exponent base as +//! a `u32`. +//! - [`EXPONENT_BASE`]: Mask to extract the exponent base: the base the +//! exponent is raised to. +//! - [`EXPONENT_RADIX_SHIFT`]: Shift to convert to and from an exponent radix +//! as a `u32`. +//! - [`EXPONENT_RADIX`]: Mask to extract the exponent radix: the radix for the +//! exponent digits. +//! +//! [`DIGIT_SEPARATOR_SHIFT`]: DIGIT_SEPARATOR_SHIFT +//! [`DIGIT_SEPARATOR`]: NumberFormat::DIGIT_SEPARATOR +//! [`BASE_PREFIX_SHIFT`]: BASE_PREFIX_SHIFT +//! [`BASE_PREFIX`]: NumberFormat::BASE_PREFIX +//! [`BASE_SUFFIX_SHIFT`]: BASE_SUFFIX_SHIFT +//! [`BASE_SUFFIX`]: NumberFormat::BASE_SUFFIX +//! [`MANTISSA_RADIX_SHIFT`]: MANTISSA_RADIX_SHIFT +//! [`MANTISSA_RADIX`]: NumberFormat::MANTISSA_RADIX +//! [`RADIX_SHIFT`]: RADIX_SHIFT +//! [`RADIX`]: NumberFormat::RADIX +//! [`EXPONENT_BASE_SHIFT`]: EXPONENT_BASE_SHIFT +//! [`EXPONENT_BASE`]: NumberFormat::EXPONENT_BASE +//! [`EXPONENT_RADIX_SHIFT`]: EXPONENT_RADIX_SHIFT +//! [`EXPONENT_RADIX`]: crate::NumberFormat::EXPONENT_RADIX +//! +//! ## Character Functions +//! +//! Functions to get control characters from the format packed struct. +//! +//! - [`digit_separator`]: Extract the digit separator from the format packed +//! struct. +//! - [`base_prefix`]: Extract the base prefix character from the format packed +//! struct. +//! - [`base_suffix`]: Extract the base suffix character from the format packed +//! struct. +//! - [`mantissa_radix`]: Extract the mantissa radix from the format packed +//! struct. +//! - [`exponent_base`]: Extract the exponent base from the format packed +//! struct. +//! - [`exponent_radix`]: Extract the exponent radix from the format packed +//! struct. +//! +//! ## Validators +//! +//! Functions to validate control characters for the format packed struct. +//! +//! - [`is_valid_exponent_flags`]: Determine if the provided exponent flags are +//! valid. +//! - [`is_valid_digit_separator`]: Determine if the digit separator is valid. +//! - [`is_valid_base_prefix`]: Determine if the base prefix character is valid. +//! - [`is_valid_base_suffix`]: Determine if the base suffix character is valid. +//! - [`is_valid_punctuation`]: Determine if all of the "punctuation" characters +//! are valid. +//! - [`is_valid_radix`]: Determine if the radix is valid. +//! +//! +#![cfg_attr( + feature = "format", + doc = " +[`Rust`]: https://www.rust-lang.org/ +[`Python`]: https://www.python.org/ +[`Python3`]: https://www.python.org/ +[`Python3.6`]: https://www.python.org/downloads/release/python-360/ +[`Python3.5`]: https://www.python.org/downloads/release/python-350/ +[`Python2`]: https://www.python.org/downloads/release/python-270/ +[`C++`]: https://en.cppreference.com/w/ +[`C++20`]: https://en.cppreference.com/w/cpp/20 +[`C++17`]: https://en.cppreference.com/w/cpp/17 +[`C++14`]: https://en.cppreference.com/w/cpp/14 +[`C++11`]: https://en.cppreference.com/w/cpp/11 +[`C++03`]: https://en.wikipedia.org/wiki/C%2B%2B03 +[`C++98`]: https://en.cppreference.com/w/ +[`C`]: https://en.cppreference.com/w/c +[`C18`]: https://en.cppreference.com/w/c/17 +[`C11`]: https://en.cppreference.com/w/c/11 +[`C99`]: https://en.cppreference.com/w/c/99 +[`C90`]: https://en.cppreference.com/w/c +[`C89`]: https://en.cppreference.com/w/c +[`Ruby`]: https://www.ruby-lang.org/en/ +[`Swift`]: https://developer.apple.com/swift/ +[`Golang`]: https://go.dev/ +[`Haskell`]: https://www.haskell.org/ +[`Javascript`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript +[`Perl`]: https://www.perl.org/ +[`PHP`]: https://www.php.net/ +[`Java`]: https://www.java.com/en/ +[`R`]: https://www.r-project.org/ +[`Kotlin`]: https://kotlinlang.org/ +[`Julia`]: https://julialang.org/ +[`C#`]: https://learn.microsoft.com/en-us/dotnet/csharp/ +[`C#7`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-73 +[`C#6`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-60 +[`C#5`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-50 +[`C#4`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-40 +[`C#3`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-30 +[`C#2`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-20 +[`C#1`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-12-1 +[`Kawa`]: https://www.gnu.org/software/kawa/ +[`Gambit-C`]: https://gambitscheme.org/ +[`Guile`]: https://www.gnu.org/software/guile/ +[`Clojure`]: https://clojure.org/ +[`Erlang`]: https://www.erlang.org/ +[`Elm`]: https://elm-lang.org/ +[`Scala`]: https://www.scala-lang.org/ +[`Elixir`]: https://elixir-lang.org/ +[`FORTRAN`]: https://fortran-lang.org/ +[`D`]: https://dlang.org/ +[`Coffeescript`]: https://coffeescript.org/ +[`Cobol`]: https://www.ibm.com/think/topics/cobol +[`F#`]: https://fsharp.org/ +[`Visual Basic`]: https://learn.microsoft.com/en-us/dotnet/visual-basic/ +[`OCaml`]: https://ocaml.org/ +[`Objective-C`]: https://en.wikipedia.org/wiki/Objective-C +[`ReasonML`]: https://reasonml.github.io/ +[`Octave`]: https://octave.org/ +[`Matlab`]: https://www.mathworks.com/products/matlab.html +[`Zig`]: https://ziglang.org/ +[`Sage`]: https://www.sagemath.org/ +[`JSON-REF`]: https://www.json.org/json-en.html +[`TOML-REF`]: https://toml.io/en/ +[`YAML-REF`]: https://yaml.org/ +[`XML-REF`]: https://en.wikipedia.org/wiki/XML +[`SQLite`]: https://www.sqlite.org/ +[`PostgreSQL`]: https://www.postgresql.org/ +[`MySQL`]: https://www.mysql.com/ +[`MongoDB`]: https://www.mongodb.com/ +" +)] + +use crate::error::Error; +#[cfg(feature = "format")] +pub use crate::feature_format::*; +pub use crate::format_builder::*; +pub use crate::format_flags::*; +#[cfg(not(feature = "format"))] +pub use crate::not_feature_format::*; +#[cfg(feature = "format")] +pub use crate::prebuilt_formats::*; + +/// Determine if the format packed struct is valid. +#[inline(always)] +pub const fn format_is_valid() -> bool { + NumberFormat:: {}.is_valid() +} + +/// Get the error type from the format packed struct. +/// +/// An error type of `Error::Success` means the format is valid, any +/// other error signifies an invalid format. +#[inline(always)] +pub const fn format_error() -> Error { + NumberFormat:: {}.error() +} + +/// Standard number format. This is identical to the Rust string format. +pub const STANDARD: u128 = NumberFormatBuilder::new().build_strict(); diff --git a/rust/vendor/lexical-util/src/format_builder.rs b/rust/vendor/lexical-util/src/format_builder.rs new file mode 100644 index 0000000..f7a8f33 --- /dev/null +++ b/rust/vendor/lexical-util/src/format_builder.rs @@ -0,0 +1,3374 @@ +//! Builder for the number format. + +use core::num; + +use crate::error::Error; +use crate::format_flags as flags; + +// NOTE: The size of `Option` is guaranteed to be the same. +// https://doc.rust-lang.org/std/num/type.NonZeroUsize.html +/// Type with the exact same size as a `u8`. +#[doc(hidden)] +pub type OptionU8 = Option; + +/// Add single flag to `SyntaxFormat`. +macro_rules! add_flag { + ($format:ident, $bool:expr, $flag:ident) => { + if $bool { + $format |= flags::$flag; + } + }; +} + +/// Add multiple flags to `SyntaxFormat`. +macro_rules! add_flags { + ($format:ident ; $($bool:expr, $flag:ident ;)*) => {{ + $(add_flag!($format, $bool, $flag);)* + }}; +} + +/// Determine if a flag is set in the format. +macro_rules! has_flag { + ($format:ident, $flag:ident) => { + $format & flags::$flag != 0 + }; +} + +/// Unwrap `Option` as a const fn. +#[inline(always)] +const fn unwrap_or_zero(option: OptionU8) -> u8 { + match option { + Some(x) => x.get(), + None => 0, + } +} + +/// Validating builder for [`NumberFormat`] from the provided specifications. +/// +/// Some of the core functionality includes support for: +/// - Digit separators: ignored characters used to make numbers more readable, +/// such as `100,000`. +/// - Non-decimal radixes: writing or parsing numbers written in binary, +/// hexadecimal, or other bases. +/// - Special numbers: disabling support for special floating-point, such as +/// [`NaN`][f64::NAN]. +/// - Number components: require signs, significant digits, and more. +/// +/// Returns [`NumberFormat`] on calling [`build_strict`] if it was able to +/// compile the format, otherwise, returns [`None`]. +/// +/// [`NumberFormat`]: crate::NumberFormat +/// [`build_strict`]: Self::build_strict +/// +/// # Examples +/// +/// To create a format valid for Rust number literals, we can use the builder +/// API: +/// +/// ```rust +/// # #[cfg(feature = "format")] { +/// use core::num; +/// +/// use lexical_util::{NumberFormat, NumberFormatBuilder}; +/// +/// // create the format for literal Rust floats +/// const RUST: u128 = NumberFormatBuilder::new() +/// .digit_separator(num::NonZeroU8::new(b'_')) +/// .required_digits(true) +/// .no_positive_mantissa_sign(true) +/// .no_special(true) +/// .internal_digit_separator(true) +/// .trailing_digit_separator(true) +/// .consecutive_digit_separator(true) +/// .build_strict(); +/// +/// // then, access the formats's properties +/// let format = NumberFormat::<{ RUST }> {}; +/// assert!(format.no_positive_mantissa_sign()); +/// assert!(format.no_special()); +/// assert!(format.internal_digit_separator()); +/// assert!(format.trailing_digit_separator()); +/// assert!(format.consecutive_digit_separator()); +/// assert!(!format.no_exponent_notation()); +/// # } +/// ``` +/// +/// # Fields +/// +/// - [`digit_separator`]: Character to separate digits. +/// - [`mantissa_radix`]: Radix for mantissa digits. +/// - [`exponent_base`]: Base for the exponent. +/// - [`exponent_radix`]: Radix for the exponent digits. +/// - [`base_prefix`]: Optional character for the base prefix. +/// - [`base_suffix`]: Optional character for the base suffix. +/// - [`required_integer_digits`]: If digits are required before the decimal +/// point. +/// - [`required_fraction_digits`]: If digits are required after the decimal +/// point. +/// - [`required_exponent_digits`]: If digits are required after the exponent +/// character. +/// - [`required_mantissa_digits`]: If at least 1 significant digit is required. +/// - [`no_positive_mantissa_sign`]: If positive sign before the mantissa is not +/// allowed. +/// - [`required_mantissa_sign`]: If positive sign before the mantissa is +/// required. +/// - [`no_exponent_notation`]: If exponent notation is not allowed. +/// - [`no_positive_exponent_sign`]: If positive sign before the exponent is not +/// allowed. +/// - [`required_exponent_sign`]: If sign before the exponent is required. +/// - [`no_exponent_without_fraction`]: If exponent without fraction is not +/// allowed. +/// - [`no_special`]: If special (non-finite) values are not allowed. +/// - [`case_sensitive_special`]: If special (non-finite) values are +/// case-sensitive. +/// - [`no_integer_leading_zeros`]: If leading zeros before an integer are not +/// allowed. +/// - [`no_float_leading_zeros`]: If leading zeros before a float are not +/// allowed. +/// - [`required_exponent_notation`]: If exponent notation is required. +/// - [`case_sensitive_exponent`]: If exponent characters are case-sensitive. +/// - [`case_sensitive_base_prefix`]: If base prefixes are case-sensitive. +/// - [`case_sensitive_base_suffix`]: If base suffixes are case-sensitive. +/// - [`integer_internal_digit_separator`]: If digit separators are allowed +/// between integer digits. +/// - [`fraction_internal_digit_separator`]: If digit separators are allowed +/// between fraction digits. +/// - [`exponent_internal_digit_separator`]: If digit separators are allowed +/// between exponent digits. +/// - [`integer_leading_digit_separator`]: If a digit separator is allowed +/// before any integer digits. +/// - [`fraction_leading_digit_separator`]: If a digit separator is allowed +/// before any fraction digits. +/// - [`exponent_leading_digit_separator`]: If a digit separator is allowed +/// before any exponent digits. +/// - [`integer_trailing_digit_separator`]: If a digit separator is allowed +/// after any integer digits. +/// - [`fraction_trailing_digit_separator`]: If a digit separator is allowed +/// after any fraction digits. +/// - [`exponent_trailing_digit_separator`]: If a digit separator is allowed +/// after any exponent digits. +/// - [`integer_consecutive_digit_separator`]: If multiple consecutive integer +/// digit separators are allowed. +/// - [`fraction_consecutive_digit_separator`]: If multiple consecutive fraction +/// digit separators are allowed. +/// - [`special_digit_separator`]: If any digit separators are allowed in +/// special (non-finite) values. +/// +/// # Write Integer Fields +/// +/// No fields are used for writing integers. +/// +/// # Parse Integer Fields +/// +/// These fields are used for parsing integers: +/// +/// - [`digit_separator`]: Character to separate digits. +/// - [`mantissa_radix`]: Radix for mantissa digits. +/// - [`base_prefix`]: Optional character for the base prefix. +/// - [`base_suffix`]: Optional character for the base suffix. +/// - [`no_positive_mantissa_sign`]: If positive sign before the mantissa is not +/// allowed. +/// - [`required_mantissa_sign`]: If positive sign before the mantissa is +/// required. +/// - [`no_integer_leading_zeros`]: If leading zeros before an integer are not +/// allowed. +/// - [`integer_internal_digit_separator`]: If digit separators are allowed +/// between integer digits. +/// - [`integer_leading_digit_separator`]: If a digit separator is allowed +/// before any integer digits. +/// - [`integer_trailing_digit_separator`]: If a digit separator is allowed +/// after any integer digits. +/// - [`integer_consecutive_digit_separator`]: If multiple consecutive integer +/// digit separators are allowed. +/// +/// # Write Float Fields +/// +/// These fields are used for writing floats: +/// +/// - [`mantissa_radix`]: Radix for mantissa digits. +/// - [`exponent_base`]: Base for the exponent. +/// - [`exponent_radix`]: Radix for the exponent digits. +/// - [`no_positive_mantissa_sign`]: If positive sign before the mantissa is not +/// allowed. +/// - [`required_mantissa_sign`]: If positive sign before the mantissa is +/// required. +/// - [`no_exponent_notation`]: If exponent notation is not allowed. +/// - [`no_positive_exponent_sign`]: If positive sign before the exponent is not +/// allowed. +/// - [`required_exponent_sign`]: If sign before the exponent is required. +/// - [`required_exponent_notation`]: If exponent notation is required. +/// +/// # Parse Float Fields +/// +/// These fields are used for parsing floats: +/// +/// - [`digit_separator`]: Character to separate digits. +/// - [`mantissa_radix`]: Radix for mantissa digits. +/// - [`exponent_base`]: Base for the exponent. +/// - [`exponent_radix`]: Radix for the exponent digits. +/// - [`base_prefix`]: Optional character for the base prefix. +/// - [`base_suffix`]: Optional character for the base suffix. +/// - [`required_mantissa_digits`]: If at least 1 significant digit is required. +/// - [`required_integer_digits`]: If digits are required before the decimal +/// point. +/// - [`required_fraction_digits`]: If digits are required after the decimal +/// point. +/// - [`required_exponent_digits`]: If digits are required after the exponent +/// character. +/// - [`no_positive_mantissa_sign`]: If positive sign before the mantissa is not +/// allowed. +/// - [`required_mantissa_sign`]: If positive sign before the mantissa is +/// required. +/// - [`no_exponent_notation`]: If exponent notation is not allowed. +/// - [`no_positive_exponent_sign`]: If positive sign before the exponent is not +/// allowed. +/// - [`required_exponent_sign`]: If sign before the exponent is required. +/// - [`no_exponent_without_fraction`]: If exponent without fraction is not +/// allowed. +/// - [`no_special`]: If special (non-finite) values are not allowed. +/// - [`case_sensitive_special`]: If special (non-finite) values are +/// case-sensitive. +/// - [`no_integer_leading_zeros`]: If leading zeros before an integer are not +/// allowed. +/// - [`no_float_leading_zeros`]: If leading zeros before a float are not +/// allowed. +/// - [`required_exponent_notation`]: If exponent notation is required. +/// - [`case_sensitive_exponent`]: If exponent characters are case-sensitive. +/// - [`case_sensitive_base_prefix`]: If base prefixes are case-sensitive. +/// - [`case_sensitive_base_suffix`]: If base suffixes are case-sensitive. +/// - [`integer_internal_digit_separator`]: If digit separators are allowed +/// between integer digits. +/// - [`fraction_internal_digit_separator`]: If digit separators are allowed +/// between fraction digits. +/// - [`exponent_internal_digit_separator`]: If digit separators are allowed +/// between exponent digits. +/// - [`integer_leading_digit_separator`]: If a digit separator is allowed +/// before any integer digits. +/// - [`fraction_leading_digit_separator`]: If a digit separator is allowed +/// before any fraction digits. +/// - [`exponent_leading_digit_separator`]: If a digit separator is allowed +/// before any exponent digits. +/// - [`integer_trailing_digit_separator`]: If a digit separator is allowed +/// after any integer digits. +/// - [`fraction_trailing_digit_separator`]: If a digit separator is allowed +/// after any fraction digits. +/// - [`exponent_trailing_digit_separator`]: If a digit separator is allowed +/// after any exponent digits. +/// - [`integer_consecutive_digit_separator`]: If multiple consecutive integer +/// digit separators are allowed. +/// - [`fraction_consecutive_digit_separator`]: If multiple consecutive fraction +/// digit separators are allowed. +/// - [`special_digit_separator`]: If any digit separators are allowed in +/// special (non-finite) values. +#[cfg_attr( + feature = "power-of-two", + doc = "\n +[`exponent_base`]: Self::exponent_base +[`exponent_radix`]: Self::exponent_radix +[`mantissa_radix`]: Self::mantissa_radix +" +)] +#[cfg_attr( + not(feature = "power-of-two"), + doc = "\n +[`exponent_base`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L602\n +[`exponent_radix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L610\n +[`mantissa_radix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L594\n +" +)] +#[cfg_attr( + feature = "format", + doc = "\n +[`digit_separator`]: Self::digit_separator\n +[`required_integer_digits`]: Self::required_integer_digits\n +[`required_fraction_digits`]: Self::required_fraction_digits\n +[`required_exponent_digits`]: Self::required_exponent_digits\n +[`required_mantissa_digits`]: Self::required_mantissa_digits\n +[`no_positive_mantissa_sign`]: Self::no_positive_mantissa_sign\n +[`required_mantissa_sign`]: Self::required_mantissa_sign\n +[`no_exponent_notation`]: Self::no_exponent_notation\n +[`no_positive_exponent_sign`]: Self::no_positive_exponent_sign\n +[`required_exponent_sign`]: Self::required_exponent_sign\n +[`no_exponent_without_fraction`]: Self::no_exponent_without_fraction\n +[`no_special`]: Self::no_special\n +[`case_sensitive_special`]: Self::case_sensitive_special\n +[`no_integer_leading_zeros`]: Self::no_integer_leading_zeros\n +[`no_float_leading_zeros`]: Self::no_float_leading_zeros\n +[`required_exponent_notation`]: Self::required_exponent_notation\n +[`case_sensitive_exponent`]: Self::case_sensitive_exponent\n +[`integer_internal_digit_separator`]: Self::integer_internal_digit_separator\n +[`fraction_internal_digit_separator`]: Self::fraction_internal_digit_separator\n +[`exponent_internal_digit_separator`]: Self::exponent_internal_digit_separator\n +[`integer_leading_digit_separator`]: Self::integer_leading_digit_separator\n +[`fraction_leading_digit_separator`]: Self::fraction_leading_digit_separator\n +[`exponent_leading_digit_separator`]: Self::exponent_leading_digit_separator\n +[`integer_trailing_digit_separator`]: Self::integer_trailing_digit_separator\n +[`fraction_trailing_digit_separator`]: Self::fraction_trailing_digit_separator\n +[`exponent_trailing_digit_separator`]: Self::exponent_trailing_digit_separator\n +[`integer_consecutive_digit_separator`]: Self::integer_consecutive_digit_separator\n +[`fraction_consecutive_digit_separator`]: Self::fraction_consecutive_digit_separator\n +[`special_digit_separator`]: Self::special_digit_separator\n +" +)] +#[cfg_attr( + not(feature = "format"), + doc = "\n +[`digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L579\n +[`required_integer_digits`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L634\n +[`required_fraction_digits`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L642\n +[`required_exponent_digits`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L650\n +[`required_mantissa_digits`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L658\n +[`no_positive_mantissa_sign`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L677\n +[`required_mantissa_sign`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L685\n +[`no_exponent_notation`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L693\n +[`no_positive_exponent_sign`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L701\n +[`required_exponent_sign`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L709\n +[`no_exponent_without_fraction`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L717\n +[`no_special`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L725\n +[`case_sensitive_special`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L733\n +[`no_integer_leading_zeros`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L741\n +[`no_float_leading_zeros`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L749\n +[`required_exponent_notation`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L757\n +[`case_sensitive_exponent`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L765\n +[`integer_internal_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L793\n +[`fraction_internal_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L805\n +[`exponent_internal_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L817\n +[`integer_leading_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L842\n +[`fraction_leading_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L853\n +[`exponent_leading_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L864\n +[`integer_trailing_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L888\n +[`fraction_trailing_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L899\n +[`exponent_trailing_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L910\n +[`integer_consecutive_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L931\n +[`fraction_consecutive_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L939\n +[`special_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L965\n +" +)] +#[cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = "\n +[`base_prefix`]: Self::base_prefix +[`base_suffix`]: Self::base_suffix +[`case_sensitive_base_prefix`]: Self::case_sensitive_base_prefix +[`case_sensitive_base_suffix`]: Self::case_sensitive_base_suffix +" +)] +#[cfg_attr( + not(all(feature = "format", feature = "power-of-two")), + doc = "\n +[`base_prefix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L618\n +[`base_suffix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L626\n +[`case_sensitive_base_prefix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L773\n +[`case_sensitive_base_suffix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L781\n +" +)] +pub struct NumberFormatBuilder { + digit_separator: OptionU8, + base_prefix: OptionU8, + base_suffix: OptionU8, + mantissa_radix: u8, + exponent_base: OptionU8, + exponent_radix: OptionU8, + required_integer_digits: bool, + required_fraction_digits: bool, + required_exponent_digits: bool, + required_mantissa_digits: bool, + no_positive_mantissa_sign: bool, + required_mantissa_sign: bool, + no_exponent_notation: bool, + no_positive_exponent_sign: bool, + required_exponent_sign: bool, + no_exponent_without_fraction: bool, + no_special: bool, + case_sensitive_special: bool, + no_integer_leading_zeros: bool, + no_float_leading_zeros: bool, + required_exponent_notation: bool, + case_sensitive_exponent: bool, + case_sensitive_base_prefix: bool, + case_sensitive_base_suffix: bool, + integer_internal_digit_separator: bool, + fraction_internal_digit_separator: bool, + exponent_internal_digit_separator: bool, + integer_leading_digit_separator: bool, + fraction_leading_digit_separator: bool, + exponent_leading_digit_separator: bool, + integer_trailing_digit_separator: bool, + fraction_trailing_digit_separator: bool, + exponent_trailing_digit_separator: bool, + integer_consecutive_digit_separator: bool, + fraction_consecutive_digit_separator: bool, + exponent_consecutive_digit_separator: bool, + special_digit_separator: bool, +} + +impl NumberFormatBuilder { + // CONSTRUCTORS + + /// Create new [`NumberFormatBuilder`] with default arguments. + /// + /// The default values are: + /// - [`digit_separator`][Self::get_digit_separator] - `None` + /// - [`base_prefix`][Self::get_base_prefix] - `None` + /// - [`base_suffix`][Self::get_base_suffix] - `None` + /// - [`mantissa_radix`][Self::get_mantissa_radix] - `10` + /// - [`exponent_base`][Self::get_exponent_base] - `None` + /// - [`exponent_radix`][Self::get_exponent_radix] - `None` + /// - [`required_integer_digits`][Self::get_required_integer_digits] - + /// `false` + /// - [`required_fraction_digits`][Self::get_required_fraction_digits] - + /// `false` + /// - [`required_exponent_digits`][Self::get_required_exponent_digits] - + /// `true` + /// - [`required_mantissa_digits`][Self::get_required_mantissa_digits] - + /// `true` + /// - [`no_positive_mantissa_sign`][Self::get_no_positive_mantissa_sign] - + /// `false` + /// - [`required_mantissa_sign`][Self::get_required_mantissa_sign] - `false` + /// - [`no_exponent_notation`][Self::get_no_exponent_notation] - `false` + /// - [`no_positive_exponent_sign`][Self::get_no_positive_exponent_sign] - + /// `false` + /// - [`required_exponent_sign`][Self::get_required_exponent_sign] - `false` + /// - [`no_exponent_without_fraction`][Self::get_no_exponent_without_fraction] - + /// `false` + /// - [`no_special`][Self::get_no_special] - `false` + /// - [`case_sensitive_special`][Self::get_case_sensitive_special] - `false` + /// - [`no_integer_leading_zeros`][Self::get_no_integer_leading_zeros] - + /// `false` + /// - [`no_float_leading_zeros`][Self::get_no_float_leading_zeros] - `false` + /// - [`required_exponent_notation`][Self::get_required_exponent_notation] - + /// `false` + /// - [`case_sensitive_exponent`][Self::get_case_sensitive_exponent] - + /// `false` + /// - [`case_sensitive_base_prefix`][Self::get_case_sensitive_base_prefix] - + /// `false` + /// - [`case_sensitive_base_suffix`][Self::get_case_sensitive_base_suffix] - + /// `false` + /// - [`integer_internal_digit_separator`][Self::get_integer_internal_digit_separator] - `false` + /// - [`fraction_internal_digit_separator`][Self::get_fraction_internal_digit_separator] - `false` + /// - [`exponent_internal_digit_separator`][Self::get_exponent_internal_digit_separator] - `false` + /// - [`integer_leading_digit_separator`][Self::get_integer_leading_digit_separator] - `false` + /// - [`fraction_leading_digit_separator`][Self::get_fraction_leading_digit_separator] - `false` + /// - [`exponent_leading_digit_separator`][Self::get_exponent_leading_digit_separator] - `false` + /// - [`integer_trailing_digit_separator`][Self::get_integer_trailing_digit_separator] - `false` + /// - [`fraction_trailing_digit_separator`][Self::get_fraction_trailing_digit_separator] - `false` + /// - [`exponent_trailing_digit_separator`][Self::get_exponent_trailing_digit_separator] - `false` + /// - [`integer_consecutive_digit_separator`][Self::get_integer_consecutive_digit_separator] - `false` + /// - [`fraction_consecutive_digit_separator`][Self::get_fraction_consecutive_digit_separator] - `false` + /// - [`exponent_consecutive_digit_separator`][Self::get_exponent_consecutive_digit_separator] - `false` + /// - [`special_digit_separator`][Self::get_special_digit_separator] - + /// `false` + #[inline(always)] + pub const fn new() -> Self { + Self { + digit_separator: None, + base_prefix: None, + base_suffix: None, + mantissa_radix: 10, + exponent_base: None, + exponent_radix: None, + required_integer_digits: false, + required_fraction_digits: false, + required_exponent_digits: true, + required_mantissa_digits: true, + no_positive_mantissa_sign: false, + required_mantissa_sign: false, + no_exponent_notation: false, + no_positive_exponent_sign: false, + required_exponent_sign: false, + no_exponent_without_fraction: false, + no_special: false, + case_sensitive_special: false, + no_integer_leading_zeros: false, + no_float_leading_zeros: false, + required_exponent_notation: false, + case_sensitive_exponent: false, + case_sensitive_base_prefix: false, + case_sensitive_base_suffix: false, + integer_internal_digit_separator: false, + fraction_internal_digit_separator: false, + exponent_internal_digit_separator: false, + integer_leading_digit_separator: false, + fraction_leading_digit_separator: false, + exponent_leading_digit_separator: false, + integer_trailing_digit_separator: false, + fraction_trailing_digit_separator: false, + exponent_trailing_digit_separator: false, + integer_consecutive_digit_separator: false, + fraction_consecutive_digit_separator: false, + exponent_consecutive_digit_separator: false, + special_digit_separator: false, + } + } + + /// Create number format for standard, binary number. + #[cfg(feature = "power-of-two")] + pub const fn binary() -> u128 { + Self::from_radix(2) + } + + /// Create number format for standard, octal number. + #[cfg(feature = "power-of-two")] + pub const fn octal() -> u128 { + Self::from_radix(8) + } + + /// Create number format for standard, decimal number. + pub const fn decimal() -> u128 { + let mut builder = Self::new(); + builder.mantissa_radix = 10; + builder.exponent_base = num::NonZeroU8::new(10); + builder.exponent_radix = num::NonZeroU8::new(10); + builder.build_strict() + } + + /// Create number format for standard, hexadecimal number. + #[cfg(feature = "power-of-two")] + pub const fn hexadecimal() -> u128 { + Self::from_radix(16) + } + + /// Create number format from radix. + /// + ///
+ /// + /// This function will never fail even if the radix is invalid. It is up to + /// the caller to ensure the format is valid using + /// [`NumberFormat::is_valid`]. Only radixes from `2` to `36` should be + /// used. + /// + ///
+ /// + /// [`NumberFormat::is_valid`]: crate::NumberFormat::is_valid + // FIXME: Use `build_strict` when we can have a breaking change. + #[allow(deprecated)] + #[cfg(feature = "power-of-two")] + pub const fn from_radix(radix: u8) -> u128 { + Self::new() + .radix(radix) + .exponent_base(num::NonZeroU8::new(radix)) + .exponent_radix(num::NonZeroU8::new(radix)) + .build() + } + + // GETTERS + + // NOTE: This contains a lot of tests for our tables that would spam our + // documentation, so we hide them internally. See `scripts/docs.py` for + // how the tests are generated and run. This assumes the `format` and + // `radix` features are enabled. + + /// Get the digit separator for the number format. + /// + /// Digit separators are frequently used in number literals to group + /// digits: `1,000,000` is a lot more readable than `1000000`, but + /// the `,` characters should be ignored in the parsing of the number. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`None`], or no digit separators allowed. + /// + /// # Examples + /// + /// Using a digit separator of `_` (note that the validity + /// oh where a digit separator can appear depends on the other digit + /// separator flags). + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1_4` | ✔️ | + /// | `+_14` | ✔️ | + /// | `+14e3_5` | ✔️ | + /// | `1_d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn get_digit_separator(&self) -> OptionU8 { + self.digit_separator + } + + /// Get the radix for mantissa digits. + /// + /// This is only used for the significant digits, that is, the integral and + /// fractional components. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults + /// to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "10011010010" | 1234 | + /// | 3 | "1200201" | 1234 | + /// | 8 | "2322" | 1234 | + /// | 10 | "1234" | 1234 | + /// | 16 | "4d2" | 1234 | + /// | 31 | "18p" | 1234 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// - Write Integer + #[inline(always)] + pub const fn get_mantissa_radix(&self) -> u8 { + self.mantissa_radix + } + + /// Get the radix for the exponent. + /// + /// For example, in `1.234e3`, it means `1.234 * 10^3`, and the exponent + /// base here is 10. Some programming languages, like C, support hex floats + /// with an exponent base of 2, for example `0x1.8p3`, or `1.5 * 2^3`. + /// Defaults to `10`. Can only be modified with [`feature`][crate#features] + /// `power-of-two` or `radix`. Defaults to `10`. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn get_exponent_base(&self) -> OptionU8 { + self.exponent_base + } + + /// Get the radix for exponent digits. + /// + /// This is only used for the exponent digits. We assume the radix for the + /// significant digits ([`get_mantissa_radix`][Self::get_mantissa_radix]) is + /// 10 as is the exponent base. Defaults to `10`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "1.234^1100" | 1.234e9 | + /// | 3 | "1.234^110" | 1.234e9 | + /// | 8 | "1.234^14" | 1.234e9 | + /// | 10 | "1.234^12" | 1.234e9 | + /// | 16 | "1.234^c" | 1.234e9 | + /// | 31 | "1.234^c" | 1.234e9 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn get_exponent_radix(&self) -> OptionU8 { + self.exponent_radix + } + + /// Get the optional character for the base prefix. + /// + /// This character will come after a leading zero, so for example + /// setting the base prefix to `x` means that a leading `0x` will + /// be ignore, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`None`], or no base prefix allowed. + /// + /// # Examples + /// + /// Using a base prefix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `0x1` | ✔️ | + /// | `x1` | ❌ | + /// | `1` | ✔️ | + /// | `1x` | ❌ | + /// | `1x1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn get_base_prefix(&self) -> OptionU8 { + self.base_prefix + } + + /// Get the optional character for the base suffix. + /// + /// This character will at the end of the buffer, so for example + /// setting the base prefix to `x` means that a trailing `x` will + /// be ignored, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`None`], or no base suffix allowed. + /// + /// # Examples + /// + /// Using a base suffix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1x` | ✔️ | + /// | `1d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn get_base_suffix(&self) -> OptionU8 { + self.base_suffix + } + + /// Get if digits are required before the decimal point. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `0.1` | ✔️ | + /// | `1` | ✔️ | + /// | `.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_required_integer_digits(&self) -> bool { + self.required_integer_digits + } + + /// Get if digits are required after the decimal point, if the decimal point + /// is present. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1` | ✔️ | + /// | `1.` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_required_fraction_digits(&self) -> bool { + self.required_fraction_digits + } + + /// Get if digits are required after the exponent character, if the exponent + /// is present. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e+3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// | `1.1e+` | ❌ | + /// | `1.1e` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_required_exponent_digits(&self) -> bool { + self.required_exponent_digits + } + + /// Get if at least 1 significant digit is required. + /// + /// If not required, then values like `.` (`0`) are valid, but empty strings + /// are still invalid. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `.` | ✔️ | + /// | `e10` | ✔️ | + /// | `.e10` | ✔️ | + /// | | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_required_mantissa_digits(&self) -> bool { + self.required_mantissa_digits + } + + /// Get if a positive sign before the mantissa is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + #[inline(always)] + pub const fn get_no_positive_mantissa_sign(&self) -> bool { + self.no_positive_mantissa_sign + } + + /// Get if a sign symbol before the mantissa is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ❌ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + #[inline(always)] + pub const fn get_required_mantissa_sign(&self) -> bool { + self.required_mantissa_sign + } + + /// Get if exponent notation is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1.1` | ✔️ | + /// | `1.1e` | ❌ | + /// | `1.1e5` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn get_no_exponent_notation(&self) -> bool { + self.no_exponent_notation + } + + /// Get if a positive sign before the exponent is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ✔️ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn get_no_positive_exponent_sign(&self) -> bool { + self.no_positive_exponent_sign + } + + /// Get if a sign symbol before the exponent is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ❌ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn get_required_exponent_sign(&self) -> bool { + self.required_exponent_sign + } + + /// Get if an exponent without fraction is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1e3` | ❌ | + /// | `1.e3` | ❌ | + /// | `1.1e` | ✔️ | + /// | `.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_no_exponent_without_fraction(&self) -> bool { + self.no_exponent_without_fraction + } + + /// Get if special (non-finite) values are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `NaN` | ❌ | + /// | `inf` | ❌ | + /// | `-Infinity` | ❌ | + /// | `1.1e` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_no_special(&self) -> bool { + self.no_special + } + + /// Get if special (non-finite) values are case-sensitive. + /// + /// If set to [`true`], then `NaN` and `nan` are treated as the same value + /// ([Not a Number][f64::NAN]). Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_case_sensitive_special(&self) -> bool { + self.case_sensitive_special + } + + /// Get if leading zeros before an integer are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// + /// # Used For + /// + /// - Parse Integer + #[inline(always)] + pub const fn get_no_integer_leading_zeros(&self) -> bool { + self.no_integer_leading_zeros + } + + /// Get if leading zeros before a float are not allowed. + /// + /// This is before the significant digits of the float, that is, if there is + /// 1 or more digits in the integral component and the leading digit is 0, + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `01.0` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// | `0.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_no_float_leading_zeros(&self) -> bool { + self.no_float_leading_zeros + } + + /// Get if exponent notation is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ❌ | + /// | `1.0` | ❌ | + /// | `1e3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn get_required_exponent_notation(&self) -> bool { + self.required_exponent_notation + } + + /// Get if exponent characters are case-sensitive. + /// + /// If set to [`true`], then the exponent character `e` would be considered + /// the different from `E`. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_case_sensitive_exponent(&self) -> bool { + self.case_sensitive_exponent + } + + /// Get if base prefixes are case-sensitive. + /// + /// If set to [`true`], then the base prefix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn get_case_sensitive_base_prefix(&self) -> bool { + self.case_sensitive_base_prefix + } + + /// Get if base suffixes are case-sensitive. + /// + /// If set to [`true`], then the base suffix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn get_case_sensitive_base_suffix(&self) -> bool { + self.case_sensitive_base_suffix + } + + /// Get if digit separators are allowed between integer digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ✔️ | + /// | `1_` | ❌ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn get_integer_internal_digit_separator(&self) -> bool { + self.integer_internal_digit_separator + } + + /// Get if digit separators are allowed between fraction digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ✔️ | + /// | `1.1_` | ❌ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_fraction_internal_digit_separator(&self) -> bool { + self.fraction_internal_digit_separator + } + + /// Get if digit separators are allowed between exponent digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ✔️ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_exponent_internal_digit_separator(&self) -> bool { + self.exponent_internal_digit_separator + } + + /// Get if a digit separator is allowed before any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ❌ | + /// | `_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn get_integer_leading_digit_separator(&self) -> bool { + self.integer_leading_digit_separator + } + + /// Get if a digit separator is allowed before any fraction digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ❌ | + /// | `1._1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_fraction_leading_digit_separator(&self) -> bool { + self.fraction_leading_digit_separator + } + + /// Get if a digit separator is allowed before any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_exponent_leading_digit_separator(&self) -> bool { + self.exponent_leading_digit_separator + } + + /// Get if a digit separator is allowed after any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ✔️ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn get_integer_trailing_digit_separator(&self) -> bool { + self.integer_trailing_digit_separator + } + + /// Get if a digit separator is allowed after any fraction digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ✔️ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_fraction_trailing_digit_separator(&self) -> bool { + self.fraction_trailing_digit_separator + } + + /// Get if a digit separator is allowed after any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ✔️ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_exponent_trailing_digit_separator(&self) -> bool { + self.exponent_trailing_digit_separator + } + + /// Get if multiple consecutive integer digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// integer. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn get_integer_consecutive_digit_separator(&self) -> bool { + self.integer_consecutive_digit_separator + } + + /// Get if multiple consecutive fraction digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// fraction. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_fraction_consecutive_digit_separator(&self) -> bool { + self.fraction_consecutive_digit_separator + } + + /// Get if multiple consecutive exponent digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// exponent. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_exponent_consecutive_digit_separator(&self) -> bool { + self.exponent_consecutive_digit_separator + } + + /// Get if any digit separators are allowed in special (non-finite) values. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for any special floats: for example, `N__a_N_` is considered + /// the same as `NaN`. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn get_special_digit_separator(&self) -> bool { + self.special_digit_separator + } + + // SETTERS + + /// Set the digit separator for the number format. + /// + /// Digit separators are frequently used in number literals to group + /// digits: `1,000,000` is a lot more readable than `1000000`, but + /// the `,` characters should be ignored in the parsing of the number. + /// + /// Defaults to [`None`], or no digit separators allowed. + /// + /// # Examples + /// + /// Using a digit separator of `_` (note that the validity + /// oh where a digit separator can appear depends on the other digit + /// separator flags). + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1_4` | ✔️ | + /// | `+_14` | ✔️ | + /// | `+14e3_5` | ✔️ | + /// | `1_d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn digit_separator(mut self, character: OptionU8) -> Self { + self.digit_separator = character; + self + } + + /// Alias for [`mantissa radix`][Self::mantissa_radix]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// - Write Integer + #[inline(always)] + #[cfg(feature = "power-of-two")] + pub const fn radix(self, radix: u8) -> Self { + self.mantissa_radix(radix) + } + + /// Set the radix for mantissa digits. + /// + /// This is only used for the significant digits, that is, the integral and + /// fractional components. Defaults to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "10011010010" | 1234 | + /// | 3 | "1200201" | 1234 | + /// | 8 | "2322" | 1234 | + /// | 10 | "1234" | 1234 | + /// | 16 | "4d2" | 1234 | + /// | 31 | "18p" | 1234 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// - Write Integer + /// + /// + #[inline(always)] + #[cfg(feature = "power-of-two")] + pub const fn mantissa_radix(mut self, radix: u8) -> Self { + self.mantissa_radix = radix; + self + } + + /// Set the radix for the exponent. + /// + /// For example, in `1.234e3`, it means `1.234 * 10^3`, and the exponent + /// base here is 10. Some programming languages, like C, support hex floats + /// with an exponent base of 2, for example `0x1.8p3`, or `1.5 * 2^3`. + /// Defaults to `10`. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + #[cfg(feature = "power-of-two")] + pub const fn exponent_base(mut self, base: OptionU8) -> Self { + self.exponent_base = base; + self + } + + /// Set the radix for exponent digits. + /// + /// This is only used for the exponent digits. We assume the radix for the + /// significant digits ([`mantissa_radix`][Self::mantissa_radix]) is 10 as + /// is the exponent base. Defaults to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "1.234^1100" | 1.234e9 | + /// | 3 | "1.234^110" | 1.234e9 | + /// | 8 | "1.234^14" | 1.234e9 | + /// | 10 | "1.234^12" | 1.234e9 | + /// | 16 | "1.234^c" | 1.234e9 | + /// | 31 | "1.234^c" | 1.234e9 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// + /// + #[inline(always)] + #[cfg(feature = "power-of-two")] + pub const fn exponent_radix(mut self, radix: OptionU8) -> Self { + self.exponent_radix = radix; + self + } + + /// Set the optional character for the base prefix. + /// + /// This character will come after a leading zero, so for example + /// setting the base prefix to `x` means that a leading `0x` will + /// be ignore, if present. Defaults to [`None`], or no base prefix + /// allowed. + /// + /// # Examples + /// + /// Using a base prefix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `0x1` | ✔️ | + /// | `x1` | ❌ | + /// | `1` | ✔️ | + /// | `1x` | ❌ | + /// | `1x1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// + /// + #[inline(always)] + #[cfg(all(feature = "power-of-two", feature = "format"))] + pub const fn base_prefix(mut self, base_prefix: OptionU8) -> Self { + self.base_prefix = base_prefix; + self + } + + /// Set the optional character for the base suffix. + /// + /// This character will at the end of the buffer, so for example + /// setting the base prefix to `x` means that a trailing `x` will + /// be ignored, if present. Defaults to [`None`], or no base suffix + /// allowed. + /// + /// # Examples + /// + /// Using a base suffix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1x` | ✔️ | + /// | `1d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// + /// + #[inline(always)] + #[cfg(all(feature = "power-of-two", feature = "format"))] + pub const fn base_suffix(mut self, base_suffix: OptionU8) -> Self { + self.base_suffix = base_suffix; + self + } + + /// Set if digits are required before the decimal point. + /// + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `0.1` | ✔️ | + /// | `1` | ✔️ | + /// | `.1` | ❌ | + /// | `1.` | ❌ | + /// | | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn required_integer_digits(mut self, flag: bool) -> Self { + self.required_integer_digits = flag; + self + } + + /// Set if digits are required after the decimal point, if the decimal point + /// is present. + /// + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `0.1` | ✔️ | + /// | `1` | ✔️ | + /// | `.1` | ✔️ | + /// | `1.` | ❌ | + /// | | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn required_fraction_digits(mut self, flag: bool) -> Self { + self.required_fraction_digits = flag; + self + } + + /// Set if digits are required after the exponent character, if the exponent + /// is present. + /// + /// Defaults to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e+3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// | `1.1e+` | ❌ | + /// | `1.1e` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn required_exponent_digits(mut self, flag: bool) -> Self { + self.required_exponent_digits = flag; + self + } + + /// Set if at least 1 significant digit is required. + /// + /// If not required, then values like `.` (`0`) are valid, but empty strings + /// are still invalid. Defaults to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `.` | ✔️ | + /// | `e10` | ❌ | + /// | `.e10` | ❌ | + /// | | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn required_mantissa_digits(mut self, flag: bool) -> Self { + self.required_mantissa_digits = flag; + self + } + + /// Set if digits are required for all float components. + /// + /// Note that digits are **always** required for integers. Defaults + /// to requiring digits only for the mantissa and exponent. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1.1e3` | ✔️ | + /// | `1.1e` | ✔️ | + /// | `0.1` | ✔️ | + /// | `.1` | ❌ | + /// | `1.` | ❌ | + /// | `e10` | ❌ | + /// | `.1e10` | ❌ | + /// | | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn required_digits(mut self, flag: bool) -> Self { + self = self.required_integer_digits(flag); + self = self.required_fraction_digits(flag); + self = self.required_exponent_digits(flag); + self = self.required_mantissa_digits(flag); + self + } + + /// Set if a positive sign before the mantissa is not allowed. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn no_positive_mantissa_sign(mut self, flag: bool) -> Self { + self.no_positive_mantissa_sign = flag; + self + } + + /// Set if a sign symbol before the mantissa is required. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ❌ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// - Write Integer + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn required_mantissa_sign(mut self, flag: bool) -> Self { + self.required_mantissa_sign = flag; + self + } + + /// Set if exponent notation is not allowed. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1.1` | ✔️ | + /// | `1.1e` | ❌ | + /// | `1.1e5` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn no_exponent_notation(mut self, flag: bool) -> Self { + self.no_exponent_notation = flag; + self + } + + /// Set if a positive sign before the exponent is not allowed. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ✔️ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn no_positive_exponent_sign(mut self, flag: bool) -> Self { + self.no_positive_exponent_sign = flag; + self + } + + /// Set if a sign symbol before the exponent is required. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ❌ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn required_exponent_sign(mut self, flag: bool) -> Self { + self.required_exponent_sign = flag; + self + } + + /// Set if an exponent without fraction is not allowed. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1e3` | ❌ | + /// | `1.e3` | ✔️ | + /// | `1.1e` | ✔️ | + /// | `.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn no_exponent_without_fraction(mut self, flag: bool) -> Self { + self.no_exponent_without_fraction = flag; + self + } + + /// Set if special (non-finite) values are not allowed. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `NaN` | ❌ | + /// | `inf` | ❌ | + /// | `-Infinity` | ❌ | + /// | `1.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn no_special(mut self, flag: bool) -> Self { + self.no_special = flag; + self + } + + /// Set if special (non-finite) values are case-sensitive. + /// + /// If set to [`true`], then `NaN` and `nan` are treated as the same value + /// ([Not a Number][f64::NAN]). Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `nan` | ❌ | + /// | `NaN` | ✔️ | + /// | `inf` | ✔️ | + /// | `Inf` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn case_sensitive_special(mut self, flag: bool) -> Self { + self.case_sensitive_special = flag; + self + } + + /// Set if leading zeros before an integer are not allowed. + /// + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// + /// # Used For + /// + /// - Parse Integer + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn no_integer_leading_zeros(mut self, flag: bool) -> Self { + self.no_integer_leading_zeros = flag; + self + } + + /// Set if leading zeros before a float are not allowed. + /// + /// This is before the significant digits of the float, that is, if there is + /// 1 or more digits in the integral component and the leading digit is 0, + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `01.0` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// | `0.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn no_float_leading_zeros(mut self, flag: bool) -> Self { + self.no_float_leading_zeros = flag; + self + } + + /// Set if exponent notation is required. + /// + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ❌ | + /// | `1.0` | ❌ | + /// | `1e3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn required_exponent_notation(mut self, flag: bool) -> Self { + self.required_exponent_notation = flag; + self + } + + /// Set if exponent characters are case-sensitive. + /// + /// If set to [`true`], then the exponent character `e` would be considered + /// the different from `E`. Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1.1e3` | ✔️ | + /// | `1.1E3` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn case_sensitive_exponent(mut self, flag: bool) -> Self { + self.case_sensitive_exponent = flag; + self + } + + /// Set if base prefixes are case-sensitive. + /// + /// If set to [`true`], then the base prefix `x` would be considered the + /// different from `X`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a base prefix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `0x1` | ✔️ | + /// | `0X1` | ❌ | + /// | `1` | ✔️ | + /// | `1x` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// + /// + #[inline(always)] + #[cfg(all(feature = "power-of-two", feature = "format"))] + pub const fn case_sensitive_base_prefix(mut self, flag: bool) -> Self { + self.case_sensitive_base_prefix = flag; + self + } + + /// Set if base suffixes are case-sensitive. + /// + /// If set to [`true`], then the base suffix `x` would be considered the + /// different from `X`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a base prefix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1x` | ✔️ | + /// | `1X` | ❌ | + /// | `1d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// + /// + #[inline(always)] + #[cfg(all(feature = "power-of-two", feature = "format"))] + pub const fn case_sensitive_base_suffix(mut self, flag: bool) -> Self { + self.case_sensitive_base_suffix = flag; + self + } + + /// Set if digit separators are allowed between integer digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ✔️ | + /// | `1_` | ❌ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn integer_internal_digit_separator(mut self, flag: bool) -> Self { + self.integer_internal_digit_separator = flag; + self + } + + /// Set if digit separators are allowed between fraction digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ✔️ | + /// | `1.1_` | ❌ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn fraction_internal_digit_separator(mut self, flag: bool) -> Self { + self.fraction_internal_digit_separator = flag; + self + } + + /// Set if digit separators are allowed between exponent digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ✔️ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn exponent_internal_digit_separator(mut self, flag: bool) -> Self { + self.exponent_internal_digit_separator = flag; + self + } + + /// Set all internal digit separator flags. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Sets [`integer_internal_digit_separator`], + /// [`fraction_internal_digit_separator`], and + /// [`exponent_internal_digit_separator`]. + /// + /// [`integer_internal_digit_separator`]: Self::integer_internal_digit_separator + /// [`fraction_internal_digit_separator`]: Self::fraction_internal_digit_separator + /// [`exponent_internal_digit_separator`]: Self::exponent_internal_digit_separator + #[inline(always)] + #[cfg(feature = "format")] + pub const fn internal_digit_separator(mut self, flag: bool) -> Self { + self = self.integer_internal_digit_separator(flag); + self = self.fraction_internal_digit_separator(flag); + self = self.exponent_internal_digit_separator(flag); + self + } + + /// Set if a digit separator is allowed before any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ❌ | + /// | `_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn integer_leading_digit_separator(mut self, flag: bool) -> Self { + self.integer_leading_digit_separator = flag; + self + } + + /// Set if a digit separator is allowed before any fraction digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ✔️ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ❌ | + /// | `1._1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn fraction_leading_digit_separator(mut self, flag: bool) -> Self { + self.fraction_leading_digit_separator = flag; + self + } + + /// Set if a digit separator is allowed before any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn exponent_leading_digit_separator(mut self, flag: bool) -> Self { + self.exponent_leading_digit_separator = flag; + self + } + + /// Set all leading digit separator flags. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Sets + /// [`integer_leading_digit_separator`], + /// [`fraction_leading_digit_separator`], and + /// [`exponent_leading_digit_separator`]. + /// + /// [`integer_leading_digit_separator`]: Self::integer_leading_digit_separator + /// [`fraction_leading_digit_separator`]: Self::fraction_leading_digit_separator + /// [`exponent_leading_digit_separator`]: Self::exponent_leading_digit_separator + #[inline(always)] + #[cfg(feature = "format")] + pub const fn leading_digit_separator(mut self, flag: bool) -> Self { + self = self.integer_leading_digit_separator(flag); + self = self.fraction_leading_digit_separator(flag); + self = self.exponent_leading_digit_separator(flag); + self + } + + /// Set if a digit separator is allowed after any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ✔️ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn integer_trailing_digit_separator(mut self, flag: bool) -> Self { + self.integer_trailing_digit_separator = flag; + self + } + + /// Set if a digit separator is allowed after any fraction digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Defaults to [`false`]. + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ✔️ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ✔️ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn fraction_trailing_digit_separator(mut self, flag: bool) -> Self { + self.fraction_trailing_digit_separator = flag; + self + } + + /// Set if a digit separator is allowed after any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ✔️ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn exponent_trailing_digit_separator(mut self, flag: bool) -> Self { + self.exponent_trailing_digit_separator = flag; + self + } + + /// Set all trailing digit separator flags. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Sets + /// [`integer_trailing_digit_separator`], + /// [`fraction_trailing_digit_separator`], and + /// [`exponent_trailing_digit_separator`]. + /// + /// [`integer_trailing_digit_separator`]: Self::integer_trailing_digit_separator + /// [`fraction_trailing_digit_separator`]: Self::fraction_trailing_digit_separator + /// [`exponent_trailing_digit_separator`]: Self::exponent_trailing_digit_separator + #[inline(always)] + #[cfg(feature = "format")] + pub const fn trailing_digit_separator(mut self, flag: bool) -> Self { + self = self.integer_trailing_digit_separator(flag); + self = self.fraction_trailing_digit_separator(flag); + self = self.exponent_trailing_digit_separator(flag); + self + } + + /// Set if multiple consecutive integer digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// integer. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_` with only internal integer digit + /// separators being valid. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ✔️ | + /// | `1__1` | ✔️ | + /// | `1_` | ❌ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn integer_consecutive_digit_separator(mut self, flag: bool) -> Self { + self.integer_consecutive_digit_separator = flag; + self + } + + /// Set if multiple consecutive fraction digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// fraction. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_` with only internal fraction digit + /// separators being valid. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ✔️ | + /// | `1.1__1` | ✔️ | + /// | `1.1_` | ❌ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn fraction_consecutive_digit_separator(mut self, flag: bool) -> Self { + self.fraction_consecutive_digit_separator = flag; + self + } + + /// Set if multiple consecutive exponent digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// exponent. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_` with only internal exponent digit + /// separators being valid. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ✔️ | + /// | `1.1e1__1` | ✔️ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn exponent_consecutive_digit_separator(mut self, flag: bool) -> Self { + self.exponent_consecutive_digit_separator = flag; + self + } + + /// Set all consecutive digit separator flags. + /// + /// Sets [`integer_consecutive_digit_separator`], + /// [`fraction_consecutive_digit_separator`], and + /// [`exponent_consecutive_digit_separator`]. + /// + /// [`integer_consecutive_digit_separator`]: Self::integer_consecutive_digit_separator + /// [`fraction_consecutive_digit_separator`]: Self::fraction_consecutive_digit_separator + /// [`exponent_consecutive_digit_separator`]: Self::exponent_consecutive_digit_separator + #[inline(always)] + #[cfg(feature = "format")] + pub const fn consecutive_digit_separator(mut self, flag: bool) -> Self { + self = self.integer_consecutive_digit_separator(flag); + self = self.fraction_consecutive_digit_separator(flag); + self = self.exponent_consecutive_digit_separator(flag); + self + } + + /// Set if any digit separators are allowed in special (non-finite) values. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for any special floats: for example, `N__a_N_` is considered + /// the same as `NaN`. Defaults to [`false`]. + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `nan` | ✔️ | + /// | `na_n` | ✔️ | + /// | `na_n_` | ✔️ | + /// | `na_nx` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// + /// + #[inline(always)] + #[cfg(feature = "format")] + pub const fn special_digit_separator(mut self, flag: bool) -> Self { + self.special_digit_separator = flag; + self + } + + /// Allow digit separators in all locations for all components. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for the integer, fraction, and exponent components. Defaults + /// to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + #[cfg(feature = "format")] + pub const fn digit_separator_flags(mut self, flag: bool) -> Self { + self = self.integer_digit_separator_flags(flag); + self = self.fraction_digit_separator_flags(flag); + self = self.exponent_digit_separator_flags(flag); + self = self.special_digit_separator(flag); + self + } + + /// Set all integer digit separator flag masks. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for the integer component. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + #[cfg(feature = "format")] + pub const fn integer_digit_separator_flags(mut self, flag: bool) -> Self { + self = self.integer_internal_digit_separator(flag); + self = self.integer_leading_digit_separator(flag); + self = self.integer_trailing_digit_separator(flag); + self = self.integer_consecutive_digit_separator(flag); + self + } + + /// Set all fraction digit separator flag masks. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for the fraction component. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + #[cfg(feature = "format")] + pub const fn fraction_digit_separator_flags(mut self, flag: bool) -> Self { + self = self.fraction_internal_digit_separator(flag); + self = self.fraction_leading_digit_separator(flag); + self = self.fraction_trailing_digit_separator(flag); + self = self.fraction_consecutive_digit_separator(flag); + self + } + + /// Set all exponent digit separator flag masks. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for the exponent component. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + #[cfg(feature = "format")] + pub const fn exponent_digit_separator_flags(mut self, flag: bool) -> Self { + self = self.exponent_internal_digit_separator(flag); + self = self.exponent_leading_digit_separator(flag); + self = self.exponent_trailing_digit_separator(flag); + self = self.exponent_consecutive_digit_separator(flag); + self + } + + // BUILDER + + /// Create 128-bit, packed number format struct from builder options. + /// + ///
+ /// + /// This function will never fail. It is up to the caller to ensure the + /// format is valid using [`NumberFormat::is_valid`]. + /// + ///
+ /// + /// [`NumberFormat::is_valid`]: crate::NumberFormat::is_valid + #[inline(always)] + pub const fn build_unchecked(&self) -> u128 { + let mut format: u128 = 0; + add_flags!( + format ; + self.required_integer_digits, REQUIRED_INTEGER_DIGITS ; + self.required_fraction_digits, REQUIRED_FRACTION_DIGITS ; + self.required_exponent_digits, REQUIRED_EXPONENT_DIGITS ; + self.required_mantissa_digits, REQUIRED_MANTISSA_DIGITS ; + self.no_positive_mantissa_sign, NO_POSITIVE_MANTISSA_SIGN ; + self.required_mantissa_sign, REQUIRED_MANTISSA_SIGN ; + self.no_exponent_notation, NO_EXPONENT_NOTATION ; + self.no_positive_exponent_sign, NO_POSITIVE_EXPONENT_SIGN ; + self.required_exponent_sign, REQUIRED_EXPONENT_SIGN ; + self.no_exponent_without_fraction, NO_EXPONENT_WITHOUT_FRACTION ; + self.no_special, NO_SPECIAL ; + self.case_sensitive_special, CASE_SENSITIVE_SPECIAL ; + self.no_integer_leading_zeros, NO_INTEGER_LEADING_ZEROS ; + self.no_float_leading_zeros, NO_FLOAT_LEADING_ZEROS ; + self.required_exponent_notation, REQUIRED_EXPONENT_NOTATION ; + self.case_sensitive_exponent, CASE_SENSITIVE_EXPONENT ; + self.case_sensitive_base_prefix, CASE_SENSITIVE_BASE_PREFIX ; + self.case_sensitive_base_suffix, CASE_SENSITIVE_BASE_SUFFIX ; + self.integer_internal_digit_separator, INTEGER_INTERNAL_DIGIT_SEPARATOR ; + self.fraction_internal_digit_separator, FRACTION_INTERNAL_DIGIT_SEPARATOR ; + self.exponent_internal_digit_separator, EXPONENT_INTERNAL_DIGIT_SEPARATOR ; + self.integer_leading_digit_separator, INTEGER_LEADING_DIGIT_SEPARATOR ; + self.fraction_leading_digit_separator, FRACTION_LEADING_DIGIT_SEPARATOR ; + self.exponent_leading_digit_separator, EXPONENT_LEADING_DIGIT_SEPARATOR ; + self.integer_trailing_digit_separator, INTEGER_TRAILING_DIGIT_SEPARATOR ; + self.fraction_trailing_digit_separator, FRACTION_TRAILING_DIGIT_SEPARATOR ; + self.exponent_trailing_digit_separator, EXPONENT_TRAILING_DIGIT_SEPARATOR ; + self.integer_consecutive_digit_separator, INTEGER_CONSECUTIVE_DIGIT_SEPARATOR ; + self.fraction_consecutive_digit_separator, FRACTION_CONSECUTIVE_DIGIT_SEPARATOR ; + self.exponent_consecutive_digit_separator, EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR ; + self.special_digit_separator, SPECIAL_DIGIT_SEPARATOR ; + ); + if format & flags::DIGIT_SEPARATOR_FLAG_MASK != 0 { + format |= + (unwrap_or_zero(self.digit_separator) as u128) << flags::DIGIT_SEPARATOR_SHIFT; + } + format |= (unwrap_or_zero(self.base_prefix) as u128) << flags::BASE_PREFIX_SHIFT; + format |= (unwrap_or_zero(self.base_suffix) as u128) << flags::BASE_SUFFIX_SHIFT; + format |= (self.mantissa_radix as u128) << flags::MANTISSA_RADIX_SHIFT; + format |= (unwrap_or_zero(self.exponent_base) as u128) << flags::EXPONENT_BASE_SHIFT; + format |= (unwrap_or_zero(self.exponent_radix) as u128) << flags::EXPONENT_RADIX_SHIFT; + + format + } + + /// Build the packed number format, panicking if the builder is invalid. + /// + /// # Panics + /// + /// If the built format is not valid. This should always + /// be used within a const context to avoid panics at runtime. + #[inline(always)] + pub const fn build_strict(&self) -> u128 { + use crate::format::format_error_impl; + + let packed = self.build_unchecked(); + match format_error_impl(packed) { + Error::Success => packed, + error => core::panic!("{}", error.description()), + } + } + + /// Create 128-bit, packed number format struct from builder options. + /// + ///
+ /// + /// This function will never fail. It is up to the caller to ensure the + /// format is valid using [`NumberFormat::is_valid`]. This function is + /// soft-deprecated and you should prefer [`build_unchecked`] and handle + /// if the result is invalid instead, or use [`build_strict`] to panic on + /// any errors. This exists when compatibility with older Rust + /// versions was required. + /// + ///
+ /// + /// [`build_unchecked`]: Self::build_unchecked + /// [`build_strict`]: Self::build_strict + /// [`NumberFormat::is_valid`]: crate::NumberFormat::is_valid + #[inline(always)] + #[deprecated = "Use `build_strict` or `build_unchecked` instead."] + pub const fn build(&self) -> u128 { + self.build_unchecked() + } + + /// Re-create builder from format. + #[inline(always)] + pub const fn rebuild(format: u128) -> Self { + NumberFormatBuilder { + digit_separator: num::NonZeroU8::new(flags::digit_separator(format)), + base_prefix: num::NonZeroU8::new(flags::base_prefix(format)), + base_suffix: num::NonZeroU8::new(flags::base_suffix(format)), + mantissa_radix: flags::mantissa_radix(format) as u8, + exponent_base: num::NonZeroU8::new(flags::exponent_base(format) as u8), + exponent_radix: num::NonZeroU8::new(flags::exponent_radix(format) as u8), + required_integer_digits: has_flag!(format, REQUIRED_INTEGER_DIGITS), + required_fraction_digits: has_flag!(format, REQUIRED_FRACTION_DIGITS), + required_exponent_digits: has_flag!(format, REQUIRED_EXPONENT_DIGITS), + required_mantissa_digits: has_flag!(format, REQUIRED_MANTISSA_DIGITS), + no_positive_mantissa_sign: has_flag!(format, NO_POSITIVE_MANTISSA_SIGN), + required_mantissa_sign: has_flag!(format, REQUIRED_MANTISSA_SIGN), + no_exponent_notation: has_flag!(format, NO_EXPONENT_NOTATION), + no_positive_exponent_sign: has_flag!(format, NO_POSITIVE_EXPONENT_SIGN), + required_exponent_sign: has_flag!(format, REQUIRED_EXPONENT_SIGN), + no_exponent_without_fraction: has_flag!(format, NO_EXPONENT_WITHOUT_FRACTION), + no_special: has_flag!(format, NO_SPECIAL), + case_sensitive_special: has_flag!(format, CASE_SENSITIVE_SPECIAL), + no_integer_leading_zeros: has_flag!(format, NO_INTEGER_LEADING_ZEROS), + no_float_leading_zeros: has_flag!(format, NO_FLOAT_LEADING_ZEROS), + required_exponent_notation: has_flag!(format, REQUIRED_EXPONENT_NOTATION), + case_sensitive_exponent: has_flag!(format, CASE_SENSITIVE_EXPONENT), + case_sensitive_base_prefix: has_flag!(format, CASE_SENSITIVE_BASE_PREFIX), + case_sensitive_base_suffix: has_flag!(format, CASE_SENSITIVE_BASE_SUFFIX), + integer_internal_digit_separator: has_flag!(format, INTEGER_INTERNAL_DIGIT_SEPARATOR), + fraction_internal_digit_separator: has_flag!(format, FRACTION_INTERNAL_DIGIT_SEPARATOR), + exponent_internal_digit_separator: has_flag!(format, EXPONENT_INTERNAL_DIGIT_SEPARATOR), + integer_leading_digit_separator: has_flag!(format, INTEGER_LEADING_DIGIT_SEPARATOR), + fraction_leading_digit_separator: has_flag!(format, FRACTION_LEADING_DIGIT_SEPARATOR), + exponent_leading_digit_separator: has_flag!(format, EXPONENT_LEADING_DIGIT_SEPARATOR), + integer_trailing_digit_separator: has_flag!(format, INTEGER_TRAILING_DIGIT_SEPARATOR), + fraction_trailing_digit_separator: has_flag!(format, FRACTION_TRAILING_DIGIT_SEPARATOR), + exponent_trailing_digit_separator: has_flag!(format, EXPONENT_TRAILING_DIGIT_SEPARATOR), + integer_consecutive_digit_separator: has_flag!( + format, + INTEGER_CONSECUTIVE_DIGIT_SEPARATOR + ), + fraction_consecutive_digit_separator: has_flag!( + format, + FRACTION_CONSECUTIVE_DIGIT_SEPARATOR + ), + exponent_consecutive_digit_separator: has_flag!( + format, + EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR + ), + special_digit_separator: has_flag!(format, SPECIAL_DIGIT_SEPARATOR), + } + } +} + +impl Default for NumberFormatBuilder { + #[inline(always)] + fn default() -> Self { + Self::new() + } +} diff --git a/rust/vendor/lexical-util/src/format_flags.rs b/rust/vendor/lexical-util/src/format_flags.rs new file mode 100644 index 0000000..9c6ba9c --- /dev/null +++ b/rust/vendor/lexical-util/src/format_flags.rs @@ -0,0 +1,826 @@ +//! Bitmask flags and masks for numeric formats. +//! +//! These bitflags and masks comprise a compressed struct as a 128-bit +//! integer, allowing its use in const generics. This comprises two parts: +//! flags designating which numerical components are valid in a string, +//! and masks to designate the control characters. +//! +//! The flags are designated in the lower 64 bits that modify +//! the syntax of strings that are parsed by lexical. +//! +//! Bits 8-32 are reserved for float component flags, such +//! as for example if base prefixes or postfixes are case-sensitive, +//! if leading zeros in a float are valid, etc. +//! +//! Bits 32-64 are reserved for digit separator flags. These +//! define which locations within a float or integer digit separators +//! are valid, for example, before any digits in the integer component, +//! whether consecutive digit separators are allowed, and more. +//! +//! ```text +//! 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! |I/R|F/R|E/R|M/R|+/M|R/M|e/e|+/E|R/E|e/F|S/S|S/C|N/I|N/F|R/e|e/C| +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! +//! 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! |e/P|e/S| | +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! +//! 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! |I/I|F/I|E/I|I/L|F/L|E/L|I/T|F/T|E/T|I/C|F/C|E/C|S/D| | +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! +//! 48 49 50 51 52 53 54 55 56 57 58 59 60 62 62 63 64 +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! | | +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! +//! Where: +//! Non-Digit Separator Flags: +//! I/R = Required integer digits. +//! F/R = Required fraction digits. +//! E/R = Required exponent digits. +//! M/R = Required mantissa digits. +//! +/M = No mantissa positive sign. +//! R/M = Required positive sign. +//! e/e = No exponent notation. +//! +/E = No exponent positive sign. +//! R/E = Required exponent sign. +//! e/F = No exponent without fraction. +//! S/S = No special (non-finite) values. +//! S/C = Case-sensitive special (non-finite) values. +//! N/I = No integer leading zeros. +//! N/F = No float leading zeros. +//! R/e = Required exponent characters. +//! e/C = Case-sensitive exponent character. +//! e/P = Case-sensitive base prefix. +//! e/S = Case-sensitive base suffix. +//! +//! Digit Separator Flags: +//! I/I = Integer internal digit separator. +//! F/I = Fraction internal digit separator. +//! E/I = Exponent internal digit separator. +//! I/L = Integer leading digit separator. +//! F/L = Fraction leading digit separator. +//! E/L = Exponent leading digit separator. +//! I/T = Integer trailing digit separator. +//! F/T = Fraction trailing digit separator. +//! E/T = Exponent trailing digit separator. +//! I/C = Integer consecutive digit separator. +//! F/C = Fraction consecutive digit separator. +//! E/C = Exponent consecutive digit separator. +//! S/D = Special (non-finite) digit separator. +//! ``` +//! +//! The upper 64-bits are designated for control characters and radixes, +//! such as the digit separator and base prefix characters, radixes, +//! and more. +//! +//! ```text +//! 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! | Digit Separator | | +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! +//! 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! | | Base Prefix | | +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! +//! 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! | Base Suffix | | Mantissa Radix | | +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! +//! 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! | Exponent Base | | Exponent Radix | | +//! +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +//! ``` +//! +//! +//! Note: +//! ----- +//! +//! In order to limit the format specification and avoid parsing +//! non-numerical data, all number formats require some significant +//! digits. Examples of always invalid numbers include: +//! - ` ` +//! - `.` +//! - `e` +//! - `e7` +//! +//! Test Cases: +//! ----------- +//! +//! The following test-cases are used to define whether a literal or +//! a string float is valid in a given language, and these tests are +//! used to denote features in pre-defined formats. Only a few +//! of these flags may modify the parsing behavior of integers. +//! Integer parsing is assumed to be derived from float parsing, +//! so if consecutive digit separators are valid in the integer +//! component of a float, they are also valid in an integer. +//! +//! ```text +//! 0: '.3' // Non-required integer. +//! 1: '3.' // Non-required fraction. +//! 2: '3e' // Non-required exponent. +//! 3. '+3.0' // Mantissa positive sign. +//! 4: '3.0e7' // Exponent notation. +//! 5: '3.0e+7' // Exponent positive sign. +//! 6. '3e7' // Exponent notation without fraction. +//! 7: 'NaN' // Special (non-finite) values. +//! 8: 'NAN' // Case-sensitive special (non-finite) values. +//! 9: '3_4.01' // Integer internal digit separator. +//! A: '3.0_1' // Fraction internal digit separator. +//! B: '3.0e7_1' // Exponent internal digit separator. +//! C: '_3.01' // Integer leading digit separator. +//! D: '3._01' // Fraction leading digit separator. +//! E: '3.0e_71' // Exponent leading digit separator. +//! F: '3_.01' // Integer trailing digit separator. +//! G: '3.01_' // Fraction trailing digit separator. +//! H: '3.0e71_' // Exponent trailing digit separator. +//! I: '3__4.01' // Integer consecutive digit separator. +//! J: '3.0__1' // Fraction consecutive digit separator. +//! K: '3.0e7__1' // Exponent consecutive digit separator. +//! L: 'In_f' // Special (non-finite) digit separator. +//! M: '010' // No integer leading zeros. +//! N: '010.0' // No float leading zeros. +//! O: '1.0' // No required exponent notation. +//! P: '3.0E7' // Case-insensitive exponent character. +//! P: '0x3.0' // Case-insensitive base prefix. +//! P: '3.0H' // Case-insensitive base postfix. +//! ``` +//! +//! Currently Supported Programming and Data Languages: +//! --------------------------------------------------- +//! +//! 1. `Rust` +//! 2. `Python` +//! 3. `C++` (98, 03, 11, 14, 17) +//! 4. `C` (89, 90, 99, 11, 18) +//! 5. `Ruby` +//! 6. `Swift` +//! 7. `Go` +//! 8. `Haskell` +//! 9. `Javascript` +//! 10. `Perl` +//! 11. `PHP` +//! 12. `Java` +//! 13. `R` +//! 14. `Kotlin` +//! 15. `Julia` +//! 16. `C#` (ISO-1, ISO-2, 3, 4, 5, 6, 7) +//! 17. `Kawa` +//! 18. `Gambit-C` +//! 19. `Guile` +//! 20. `Clojure` +//! 21. `Erlang` +//! 22. `Elm` +//! 23. `Scala` +//! 24. `Elixir` +//! 25. `FORTRAN` +//! 26. `D` +//! 27. `Coffeescript` +//! 28. `Cobol` +//! 29. `F#` +//! 30. `Visual Basic` +//! 31. `OCaml` +//! 32. `Objective-C` +//! 33. `ReasonML` +//! 34. `Octave` +//! 35. `Matlab` +//! 36. `Zig` +//! 37. `SageMath` +//! 38. `JSON` +//! 39. `TOML` +//! 40. `XML` +//! 41. `SQLite` +//! 42. `PostgreSQL` +//! 43. `MySQL` +//! 44. `MongoDB` + +#![cfg_attr(rustfmt, rustfmt::skip)] +#![doc(hidden)] + +// ASSERTIONS +// ---------- + +// Ensure all our bit flags are valid. +macro_rules! check_subsequent_flags { + ($x:ident, $y:ident) => { + const _: () = assert!($x << 1 == $y); + }; +} + +// Ensure all our bit masks don't overlap. +macro_rules! check_subsequent_masks { + ($x:ident, $y:ident) => { + const _: () = assert!($x & $y == 0); + }; +} + +// Check all our masks are in the range `[0, 255]` after shifting. +macro_rules! check_mask_shifts { + ($mask:ident, $shift:ident) => { + const _: () = assert!(0 < $mask >> $shift && 255 >= $mask >> $shift); + }; +} + +// Ensure all our bit masks don't overlap with existing flags. +macro_rules! check_masks_and_flags { + ($x:ident, $y:ident) => { + const _: () = assert!($x & $y == 0); + }; +} + +// NON-DIGIT SEPARATOR FLAGS & MASKS +// --------------------------------- + +/// Digits are required before the decimal point. +pub const REQUIRED_INTEGER_DIGITS: u128 = 1 << 0; + +/// Digits are required after the decimal point. +/// This check will only occur if the decimal point is present. +pub const REQUIRED_FRACTION_DIGITS: u128 = 1 << 1; + +/// Digits are required after the exponent character. +/// This check will only occur if the exponent character is present. +pub const REQUIRED_EXPONENT_DIGITS: u128 = 1 << 2; + +/// Mantissa digits are required (either before or after the decimal point). +pub const REQUIRED_MANTISSA_DIGITS: u128 = 1 << 3; + +/// At least 1 digit in the number is required. +pub const REQUIRED_DIGITS: u128 = + REQUIRED_INTEGER_DIGITS | + REQUIRED_FRACTION_DIGITS | + REQUIRED_EXPONENT_DIGITS | + REQUIRED_MANTISSA_DIGITS; + +/// Positive sign before the mantissa is not allowed. +pub const NO_POSITIVE_MANTISSA_SIGN: u128 = 1 << 4; + +/// Positive sign before the mantissa is required. +pub const REQUIRED_MANTISSA_SIGN: u128 = 1 << 5; + +/// Exponent notation is not allowed. +pub const NO_EXPONENT_NOTATION: u128 = 1 << 6; + +/// Positive sign before the exponent is not allowed. +pub const NO_POSITIVE_EXPONENT_SIGN: u128 = 1 << 7; + +/// Positive sign before the exponent is required. +pub const REQUIRED_EXPONENT_SIGN: u128 = 1 << 8; + +/// Exponent without a fraction component is not allowed. +/// +/// This only checks if a decimal point precedes the exponent character. +/// To require fraction digits or exponent digits with this check, +/// please use the appropriate flags. +pub const NO_EXPONENT_WITHOUT_FRACTION: u128 = 1 << 9; + +/// Special (non-finite) values are not allowed. +pub const NO_SPECIAL: u128 = 1 << 10; + +/// Special (non-finite) values are case-sensitive. +pub const CASE_SENSITIVE_SPECIAL: u128 = 1 << 11; + +/// Leading zeros before an integer value are not allowed. +/// +/// If the value is a literal, then this distinction applies +/// when the value is treated like an integer literal, typically +/// when there is no decimal point. If the value is parsed, +/// then this distinction applies when the value as parsed +/// as an integer. +/// +/// # Warning +/// +/// This also does not mean that the value parsed will be correct, +/// for example, in languages like C, this will not auto- +/// deduce that the radix is 8 with leading zeros, for an octal +/// literal. +pub const NO_INTEGER_LEADING_ZEROS: u128 = 1 << 12; + +/// Leading zeros before a float value are not allowed. +/// +/// If the value is a literal, then this distinction applies +/// when the value is treated like an integer float, typically +/// when there is a decimal point. If the value is parsed, +/// then this distinction applies when the value as parsed +/// as a float. +/// +/// # Warning +/// +/// This also does not mean that the value parsed will be correct, +/// for example, in languages like C, this will not auto- +/// deduce that the radix is 8 with leading zeros, for an octal +/// literal. +pub const NO_FLOAT_LEADING_ZEROS: u128 = 1 << 13; + +/// Exponent notation is required. +/// +/// Valid floats must contain an exponent notation character, and if +/// applicable, a sign character and digits afterwards. +pub const REQUIRED_EXPONENT_NOTATION: u128 = 1 << 14; + +/// Exponent characters are case-sensitive. +pub const CASE_SENSITIVE_EXPONENT: u128 = 1 << 15; + +/// Base prefixes are case-sensitive. +pub const CASE_SENSITIVE_BASE_PREFIX: u128 = 1 << 16; + +/// Base suffixes are case-sensitive. +pub const CASE_SENSITIVE_BASE_SUFFIX: u128 = 1 << 17; + +// Non-digit separator flags. +const _: () = assert!(REQUIRED_INTEGER_DIGITS == 1); +check_subsequent_flags!(REQUIRED_INTEGER_DIGITS, REQUIRED_FRACTION_DIGITS); +check_subsequent_flags!(REQUIRED_FRACTION_DIGITS, REQUIRED_EXPONENT_DIGITS); +check_subsequent_flags!(REQUIRED_EXPONENT_DIGITS, REQUIRED_MANTISSA_DIGITS); +check_subsequent_flags!(REQUIRED_MANTISSA_DIGITS, NO_POSITIVE_MANTISSA_SIGN); +check_subsequent_flags!(NO_POSITIVE_MANTISSA_SIGN, REQUIRED_MANTISSA_SIGN); +check_subsequent_flags!(REQUIRED_MANTISSA_SIGN, NO_EXPONENT_NOTATION); +check_subsequent_flags!(NO_EXPONENT_NOTATION, NO_POSITIVE_EXPONENT_SIGN); +check_subsequent_flags!(NO_POSITIVE_EXPONENT_SIGN, REQUIRED_EXPONENT_SIGN); +check_subsequent_flags!(REQUIRED_EXPONENT_SIGN, NO_EXPONENT_WITHOUT_FRACTION); +check_subsequent_flags!(NO_EXPONENT_WITHOUT_FRACTION, NO_SPECIAL); +check_subsequent_flags!(NO_SPECIAL, CASE_SENSITIVE_SPECIAL); +check_subsequent_flags!(NO_SPECIAL, CASE_SENSITIVE_SPECIAL); +check_subsequent_flags!(CASE_SENSITIVE_SPECIAL, NO_INTEGER_LEADING_ZEROS); +check_subsequent_flags!(NO_INTEGER_LEADING_ZEROS, NO_FLOAT_LEADING_ZEROS); +check_subsequent_flags!(NO_FLOAT_LEADING_ZEROS, REQUIRED_EXPONENT_NOTATION); +check_subsequent_flags!(REQUIRED_EXPONENT_NOTATION, CASE_SENSITIVE_EXPONENT); +check_subsequent_flags!(CASE_SENSITIVE_EXPONENT, CASE_SENSITIVE_BASE_PREFIX); +check_subsequent_flags!(CASE_SENSITIVE_BASE_PREFIX, CASE_SENSITIVE_BASE_SUFFIX); + +// DIGIT SEPARATOR FLAGS & MASKS +// ----------------------------- + +/// Digit separators are allowed between integer digits. +pub const INTEGER_INTERNAL_DIGIT_SEPARATOR: u128 = 1 << 32; + +/// Digit separators are allowed between fraction digits. +pub const FRACTION_INTERNAL_DIGIT_SEPARATOR: u128 = 1 << 33; + +/// Digit separators are allowed between exponent digits. +pub const EXPONENT_INTERNAL_DIGIT_SEPARATOR: u128 = 1 << 34; + +/// A digit separator is allowed before any integer digits. +pub const INTEGER_LEADING_DIGIT_SEPARATOR: u128 = 1 << 35; + +/// A digit separator is allowed before any fraction digits. +pub const FRACTION_LEADING_DIGIT_SEPARATOR: u128 = 1 << 36; + +/// A digit separator is allowed before any exponent digits. +pub const EXPONENT_LEADING_DIGIT_SEPARATOR: u128 = 1 << 37; + +/// A digit separator is allowed after any integer digits. +pub const INTEGER_TRAILING_DIGIT_SEPARATOR: u128 = 1 << 38; + +/// A digit separator is allowed after any fraction digits. +pub const FRACTION_TRAILING_DIGIT_SEPARATOR: u128 = 1 << 39; + +/// A digit separator is allowed after any exponent digits. +pub const EXPONENT_TRAILING_DIGIT_SEPARATOR: u128 = 1 << 40; + +/// Multiple consecutive integer digit separators are allowed. +pub const INTEGER_CONSECUTIVE_DIGIT_SEPARATOR: u128 = 1 << 41; + +/// Multiple consecutive fraction digit separators are allowed. +pub const FRACTION_CONSECUTIVE_DIGIT_SEPARATOR: u128 = 1 << 42; + +/// Multiple consecutive exponent digit separators are allowed. +pub const EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR: u128 = 1 << 43; + +/// Digit separators are allowed between digits. +pub const INTERNAL_DIGIT_SEPARATOR: u128 = + INTEGER_INTERNAL_DIGIT_SEPARATOR | + FRACTION_INTERNAL_DIGIT_SEPARATOR | + EXPONENT_INTERNAL_DIGIT_SEPARATOR; + +/// A digit separator is allowed before any digits. +pub const LEADING_DIGIT_SEPARATOR: u128 = + INTEGER_LEADING_DIGIT_SEPARATOR | + FRACTION_LEADING_DIGIT_SEPARATOR | + EXPONENT_LEADING_DIGIT_SEPARATOR; + +/// A digit separator is allowed after any digits. +pub const TRAILING_DIGIT_SEPARATOR: u128 = + INTEGER_TRAILING_DIGIT_SEPARATOR | + FRACTION_TRAILING_DIGIT_SEPARATOR | + EXPONENT_TRAILING_DIGIT_SEPARATOR; + +/// Multiple consecutive digit separators are allowed. +pub const CONSECUTIVE_DIGIT_SEPARATOR: u128 = + INTEGER_CONSECUTIVE_DIGIT_SEPARATOR | + FRACTION_CONSECUTIVE_DIGIT_SEPARATOR | + EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR; + +/// Any digit separators are allowed in special (non-finite) values. +pub const SPECIAL_DIGIT_SEPARATOR: u128 = 1 << 44; + +// Digit separator flags. +const _: () = assert!(INTEGER_INTERNAL_DIGIT_SEPARATOR == 1 << 32); +check_subsequent_flags!(INTEGER_INTERNAL_DIGIT_SEPARATOR, FRACTION_INTERNAL_DIGIT_SEPARATOR); +check_subsequent_flags!(FRACTION_INTERNAL_DIGIT_SEPARATOR, EXPONENT_INTERNAL_DIGIT_SEPARATOR); +check_subsequent_flags!(EXPONENT_INTERNAL_DIGIT_SEPARATOR, INTEGER_LEADING_DIGIT_SEPARATOR); +check_subsequent_flags!(INTEGER_LEADING_DIGIT_SEPARATOR, FRACTION_LEADING_DIGIT_SEPARATOR); +check_subsequent_flags!(FRACTION_LEADING_DIGIT_SEPARATOR, EXPONENT_LEADING_DIGIT_SEPARATOR); +check_subsequent_flags!(EXPONENT_LEADING_DIGIT_SEPARATOR, INTEGER_TRAILING_DIGIT_SEPARATOR); +check_subsequent_flags!(INTEGER_TRAILING_DIGIT_SEPARATOR, FRACTION_TRAILING_DIGIT_SEPARATOR); +check_subsequent_flags!(FRACTION_TRAILING_DIGIT_SEPARATOR, EXPONENT_TRAILING_DIGIT_SEPARATOR); +check_subsequent_flags!(EXPONENT_TRAILING_DIGIT_SEPARATOR, INTEGER_CONSECUTIVE_DIGIT_SEPARATOR); +check_subsequent_flags!(INTEGER_CONSECUTIVE_DIGIT_SEPARATOR, FRACTION_CONSECUTIVE_DIGIT_SEPARATOR); +check_subsequent_flags!(FRACTION_CONSECUTIVE_DIGIT_SEPARATOR, EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR); +check_subsequent_flags!(EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR, SPECIAL_DIGIT_SEPARATOR); + +// CONTROL CHARACTER & RADIX MASKS +// ------------------------------- + +/// Shift to convert to and from a digit separator as a `u8`. +pub const DIGIT_SEPARATOR_SHIFT: i32 = 64; + +/// Mask to extract the digit separator character. +pub const DIGIT_SEPARATOR: u128 = 0xFF << DIGIT_SEPARATOR_SHIFT; + +/// Shift to convert to and from a base prefix as a `u8`. +pub const BASE_PREFIX_SHIFT: i32 = 88; + +/// Mask to extract the base prefix character. +pub const BASE_PREFIX: u128 = 0xFF << BASE_PREFIX_SHIFT; + +/// Shift to convert to and from a base suffix as a `u8`. +pub const BASE_SUFFIX_SHIFT: i32 = 96; + +/// Mask to extract the base suffix character. +pub const BASE_SUFFIX: u128 = 0xFF << BASE_SUFFIX_SHIFT; + +/// Shift to convert to and from a mantissa radix as a `u32`. +pub const MANTISSA_RADIX_SHIFT: i32 = 104; + +/// Mask to extract the mantissa radix: the radix for the significant digits. +pub const MANTISSA_RADIX: u128 = 0xFF << MANTISSA_RADIX_SHIFT; + +/// Alias for [`MANTISSA_RADIX_SHIFT`]. +pub const RADIX_SHIFT: i32 = MANTISSA_RADIX_SHIFT; + +/// Alias for [`MANTISSA_RADIX`]. +pub const RADIX: u128 = MANTISSA_RADIX; + +/// Shift to convert to and from an exponent base as a `u32`. +pub const EXPONENT_BASE_SHIFT: i32 = 112; + +/// Mask to extract the exponent base: the base the exponent is raised to. +pub const EXPONENT_BASE: u128 = 0xFF << EXPONENT_BASE_SHIFT; + +/// Shift to convert to and from an exponent radix as a `u32`. +pub const EXPONENT_RADIX_SHIFT: i32 = 120; + +/// Mask to extract the exponent radix: the radix for the exponent digits. +pub const EXPONENT_RADIX: u128 = 0xFF << EXPONENT_RADIX_SHIFT; + +/// Mask to extract the exponent radix: the radix for the exponent digits. +/// +/// This only extracts the radix bits, so negating it can be used +/// to see if any other custom settings were provided. +pub const RADIX_MASK: u128 = MANTISSA_RADIX | EXPONENT_RADIX; + +// Masks do not overlap. +check_subsequent_masks!(DIGIT_SEPARATOR, BASE_PREFIX); +check_subsequent_masks!(BASE_PREFIX, BASE_SUFFIX); +check_subsequent_masks!(BASE_SUFFIX, MANTISSA_RADIX); +check_subsequent_masks!(MANTISSA_RADIX, EXPONENT_BASE); +check_subsequent_masks!(EXPONENT_BASE, EXPONENT_RADIX); + +// Check all our shifts shift the masks to a single byte. +check_mask_shifts!(DIGIT_SEPARATOR, DIGIT_SEPARATOR_SHIFT); +check_mask_shifts!(BASE_PREFIX, BASE_PREFIX_SHIFT); +check_mask_shifts!(BASE_SUFFIX, BASE_SUFFIX_SHIFT); +check_mask_shifts!(MANTISSA_RADIX, MANTISSA_RADIX_SHIFT); +check_mask_shifts!(EXPONENT_BASE, EXPONENT_BASE_SHIFT); +check_mask_shifts!(EXPONENT_RADIX, EXPONENT_RADIX_SHIFT); + +// Check masks don't overlap with neighboring flags. +check_masks_and_flags!(DIGIT_SEPARATOR, SPECIAL_DIGIT_SEPARATOR); + +// HIDDEN MASKS +// ------------ + +/// Mask to extract the flag bits. +#[doc(hidden)] +pub const FLAG_MASK: u128 = + REQUIRED_DIGITS | + NO_POSITIVE_MANTISSA_SIGN | + REQUIRED_MANTISSA_SIGN | + NO_EXPONENT_NOTATION | + NO_POSITIVE_EXPONENT_SIGN | + REQUIRED_EXPONENT_SIGN | + NO_EXPONENT_WITHOUT_FRACTION | + NO_SPECIAL | + CASE_SENSITIVE_SPECIAL | + NO_INTEGER_LEADING_ZEROS | + NO_FLOAT_LEADING_ZEROS | + REQUIRED_EXPONENT_NOTATION | + CASE_SENSITIVE_EXPONENT | + CASE_SENSITIVE_BASE_PREFIX | + CASE_SENSITIVE_BASE_SUFFIX | + INTERNAL_DIGIT_SEPARATOR | + LEADING_DIGIT_SEPARATOR | + TRAILING_DIGIT_SEPARATOR | + CONSECUTIVE_DIGIT_SEPARATOR | + SPECIAL_DIGIT_SEPARATOR; + +/// Mask to extract the flag bits controlling interface parsing. +/// +/// This mask controls all the flags handled by the interface, +/// omitting those that are handled prior. This limits the +/// number of match paths required to determine the correct +/// interface. +#[doc(hidden)] +pub const INTERFACE_FLAG_MASK: u128 = + REQUIRED_DIGITS | + NO_EXPONENT_NOTATION | + NO_POSITIVE_EXPONENT_SIGN | + REQUIRED_EXPONENT_SIGN | + NO_EXPONENT_WITHOUT_FRACTION | + NO_FLOAT_LEADING_ZEROS | + REQUIRED_EXPONENT_NOTATION | + INTERNAL_DIGIT_SEPARATOR | + LEADING_DIGIT_SEPARATOR | + TRAILING_DIGIT_SEPARATOR | + CONSECUTIVE_DIGIT_SEPARATOR; + +/// Mask to extract digit separator flags. +#[doc(hidden)] +pub const DIGIT_SEPARATOR_FLAG_MASK: u128 = + INTERNAL_DIGIT_SEPARATOR | + LEADING_DIGIT_SEPARATOR | + TRAILING_DIGIT_SEPARATOR | + CONSECUTIVE_DIGIT_SEPARATOR | + SPECIAL_DIGIT_SEPARATOR; + +/// Mask to extract exponent flags. +#[doc(hidden)] +pub const EXPONENT_FLAG_MASK: u128 = + REQUIRED_EXPONENT_DIGITS | + NO_EXPONENT_NOTATION | + NO_POSITIVE_EXPONENT_SIGN | + REQUIRED_EXPONENT_SIGN | + NO_EXPONENT_WITHOUT_FRACTION | + REQUIRED_EXPONENT_NOTATION | + EXPONENT_INTERNAL_DIGIT_SEPARATOR | + EXPONENT_LEADING_DIGIT_SEPARATOR | + EXPONENT_TRAILING_DIGIT_SEPARATOR | + EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR; + +/// Mask to extract integer digit separator flags. +#[doc(hidden)] +pub const INTEGER_DIGIT_SEPARATOR_FLAG_MASK: u128 = + INTEGER_INTERNAL_DIGIT_SEPARATOR | + INTEGER_LEADING_DIGIT_SEPARATOR | + INTEGER_TRAILING_DIGIT_SEPARATOR | + INTEGER_CONSECUTIVE_DIGIT_SEPARATOR; + +/// Mask to extract fraction digit separator flags. +#[doc(hidden)] +pub const FRACTION_DIGIT_SEPARATOR_FLAG_MASK: u128 = + FRACTION_INTERNAL_DIGIT_SEPARATOR | + FRACTION_LEADING_DIGIT_SEPARATOR | + FRACTION_TRAILING_DIGIT_SEPARATOR | + FRACTION_CONSECUTIVE_DIGIT_SEPARATOR; + +/// Mask to extract exponent digit separator flags. +#[doc(hidden)] +pub const EXPONENT_DIGIT_SEPARATOR_FLAG_MASK: u128 = + EXPONENT_INTERNAL_DIGIT_SEPARATOR | + EXPONENT_LEADING_DIGIT_SEPARATOR | + EXPONENT_TRAILING_DIGIT_SEPARATOR | + EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR; + +// EXTRACTORS +// ---------- + +/// Extract the digit separator from the format packed struct. +#[doc(hidden)] +#[inline(always)] +pub const fn digit_separator(format: u128) -> u8 { + ((format & DIGIT_SEPARATOR) >> DIGIT_SEPARATOR_SHIFT) as u8 +} + +/// Extract the base prefix character from the format packed struct. +#[doc(hidden)] +#[inline(always)] +pub const fn base_prefix(format: u128) -> u8 { + ((format & BASE_PREFIX) >> BASE_PREFIX_SHIFT) as u8 +} + +/// Extract the base suffix character from the format packed struct. +#[doc(hidden)] +#[inline(always)] +pub const fn base_suffix(format: u128) -> u8 { + ((format & BASE_SUFFIX) >> BASE_SUFFIX_SHIFT) as u8 +} + +/// Extract the mantissa radix from the format packed struct. +#[doc(hidden)] +#[inline(always)] +pub const fn mantissa_radix(format: u128) -> u32 { + ((format & MANTISSA_RADIX) >> MANTISSA_RADIX_SHIFT) as u32 +} + +/// Extract the exponent base from the format packed struct. +/// +/// If not provided, defaults to `mantissa_radix`. +#[doc(hidden)] +#[inline(always)] +pub const fn exponent_base(format: u128) -> u32 { + let radix = ((format & EXPONENT_BASE) >> EXPONENT_BASE_SHIFT) as u32; + if radix == 0 { + mantissa_radix(format) + } else { + radix + } +} + +/// Extract the exponent radix from the format packed struct. +/// +/// If not provided, defaults to `mantissa_radix`. +#[doc(hidden)] +#[inline(always)] +pub const fn exponent_radix(format: u128) -> u32 { + let radix = ((format & EXPONENT_RADIX) >> EXPONENT_RADIX_SHIFT) as u32; + if radix == 0 { + mantissa_radix(format) + } else { + radix + } +} + +/// Extract a generic radix from the format and bitflags. +#[doc(hidden)] +#[inline(always)] +pub const fn radix_from_flags(format: u128, mask: u128, shift: i32) -> u32 { + let radix = ((format & mask) >> shift) as u32; + if radix == 0 { + mantissa_radix(format) + } else { + radix + } +} + +// VALIDATORS +// ---------- + +// NOTE: All of these are only used when building formats so it doesn't matter if +// they have performance issues, since these will be built at compile time. + +/// Determine if the provided exponent flags are valid. +#[inline(always)] +pub const fn is_valid_exponent_flags(format: u128) -> bool { + // Both cannot be set. + format & NO_EXPONENT_NOTATION == 0 || format & REQUIRED_EXPONENT_NOTATION == 0 +} + +/// Determine if an optional control character is valid. +#[doc(hidden)] +#[inline(always)] +const fn is_valid_optional_control_radix(radix: u32, value: u8) -> bool { + // Validate the character isn't a digit or sign character, and is valid ASCII. + use crate::ascii::is_valid_ascii; + use crate::digit::char_is_digit_const; + !char_is_digit_const(value, radix) && + value != b'+' && + value != b'-' && + (is_valid_ascii(value) || value == 0) +} + +/// Determine if an optional control character is valid. +#[doc(hidden)] +#[inline(always)] +const fn is_valid_optional_control(format: u128, value: u8) -> bool { + // Need to get the larger of the two radix values, since these + // will be the characters that define the valid digits. + // const fn doesn't support max as of 1.55 nightly. + let mradix = mantissa_radix(format); + let eradix = exponent_radix(format); + let radix = if mradix > eradix { + mradix + } else { + eradix + }; + is_valid_optional_control_radix(radix, value) +} + +/// Determine if an control character is valid. +#[doc(hidden)] +#[inline(always)] +const fn is_valid_control(format: u128, value: u8) -> bool { + value != 0 && is_valid_optional_control(format, value) +} + +/// Determine if the digit separator is valid. +/// +/// Digit separators must not be valid digits or sign characters. +#[inline(always)] +pub const fn is_valid_digit_separator(format: u128) -> bool { + let value = digit_separator(format); + if cfg!(feature = "format") { + is_valid_optional_control(format, value) + } else { + value == 0 + } +} + +/// Determine if the base prefix character is valid. +#[inline(always)] +pub const fn is_valid_base_prefix(format: u128) -> bool { + let value = base_prefix(format); + if cfg!(all(feature = "format", feature = "power-of-two")) { + is_valid_optional_control(format, value) + } else { + value == 0 + } +} + +/// Determine if the base suffix character is valid. +#[inline(always)] +pub const fn is_valid_base_suffix(format: u128) -> bool { + let value = base_suffix(format); + if cfg!(all(feature = "format", feature = "power-of-two")) { + is_valid_optional_control(format, value) + } else { + value == 0 + } +} + +/// Determine if all of the "punctuation" characters are valid. +#[inline(always)] +#[allow(clippy::if_same_then_else)] // reason="all are different logic conditions" +pub const fn is_valid_punctuation(format: u128) -> bool { + // All the checks against optional characters with mandatory are fine: + // if they're not 0, then they can't overlap, and mandatory can't be 0. + if cfg!(not(feature = "format")) && digit_separator(format) != 0 { + // Digit separator set when not allowed. + false + } else { + let separator = digit_separator(format); + let prefix = base_prefix(format); + let suffix = base_suffix(format); + // Check all are optional, or enough are not present. + match (separator, prefix, suffix) { + (0, 0, 0) => true, + (_, 0, 0) => true, + (0, _, 0) => true, + (0, 0, _) => true, + // Can't have more than 1 0, check they're all different. + (x, y, z) => x != y && x != z && y != z, + } + } +} + +/// Determine if all of the "punctuation" characters for the options API are valid. +#[doc(hidden)] +#[inline(always)] +#[allow(clippy::if_same_then_else)] // reason="all are different logic conditions" +#[allow(clippy::needless_bool)] // reason="not needless depending on the format condition" +pub const fn is_valid_options_punctuation(format: u128, exponent: u8, decimal_point: u8) -> bool { + // All the checks against optional characters with mandatory are fine: + // if they're not 0, then they can't overlap, and mandatory can't be 0. + if !is_valid_control(format, decimal_point) || !is_valid_control(format, exponent) { + // Must be in the valid range. + false + } else if decimal_point == exponent { + // Can't have overlapping characters. + false + } else if cfg!(feature = "format") && digit_separator(format) == decimal_point { + false + } else if cfg!(feature = "format") && digit_separator(format) == exponent { + false + } else if cfg!(feature = "format") && base_prefix(format) == decimal_point { + false + } else if cfg!(feature = "format") && base_prefix(format) == exponent { + false + } else if cfg!(feature = "format") && base_suffix(format) == decimal_point { + false + } else if cfg!(feature = "format") && base_suffix(format) == exponent { + false + } else { + true + } +} + +/// Determine if the radix is valid. +#[inline(always)] +pub const fn is_valid_radix(radix: u32) -> bool { + if cfg!(feature = "radix") { + radix >= 2 && radix <= 36 + } else if cfg!(feature = "power-of-two") { + matches!(radix, 2 | 4 | 8 | 10 | 16 | 32) + } else { + radix == 10 + } +} diff --git a/rust/vendor/lexical-util/src/iterator.rs b/rust/vendor/lexical-util/src/iterator.rs new file mode 100644 index 0000000..342f56f --- /dev/null +++ b/rust/vendor/lexical-util/src/iterator.rs @@ -0,0 +1,414 @@ +//! Specialized iterator traits. +//! +//! The traits are for iterables containing bytes, and provide optimizations +//! which then can be used for contiguous or non-contiguous iterables, +//! including containers or iterators of any kind. + +#![cfg(any(feature = "parse-floats", feature = "parse-integers"))] + +use core::mem; + +// Re-export our digit iterators. +#[cfg(not(feature = "format"))] +pub use crate::noskip::{AsBytes, Bytes}; +#[cfg(feature = "format")] +pub use crate::skip::{AsBytes, Bytes}; + +/// A trait for working with iterables of bytes. +/// +/// These iterators can either be contiguous or not contiguous and provide +/// methods for reading data and accessing underlying data. The readers +/// can either be contiguous or non-contiguous, although performance and +/// some API methods may not be available for both. +/// +/// # Safety +/// +/// Safe if [`set_cursor`] is set to an index <= [`buffer_length`], so no +/// out-of-bounds reads can occur. Also, [`get_buffer`] must return a slice of +/// initialized bytes. The caller must also ensure that any calls that increment +/// the cursor, such as [`step_by_unchecked`], [`step_unchecked`], and +/// [`peek_many_unchecked`] never exceed [`buffer_length`] as well. +/// +/// [`set_cursor`]: `Iter::set_cursor` +/// [`buffer_length`]: `Iter::buffer_length` +/// [`get_buffer`]: `Iter::get_buffer` +/// [`step_by_unchecked`]: `Iter::step_by_unchecked` +/// [`step_unchecked`]: `Iter::step_unchecked` +/// [`peek_many_unchecked`]: `Iter::peek_many_unchecked` +pub unsafe trait Iter<'a> { + /// Determine if the buffer is contiguous in memory. + const IS_CONTIGUOUS: bool; + + // CURSORS + // ------- + + /// Get a ptr to the current start of the buffer. + #[inline(always)] + fn as_ptr(&self) -> *const u8 { + self.as_slice().as_ptr() + } + + /// Get a slice to the current start of the buffer. + #[inline(always)] + fn as_slice(&self) -> &'a [u8] { + debug_assert!(self.cursor() <= self.buffer_length()); + // SAFETY: safe since index must be in range. + unsafe { self.get_buffer().get_unchecked(self.cursor()..) } + } + + /// Get a slice to the full underlying contiguous buffer, + fn get_buffer(&self) -> &'a [u8]; + + /// Get the total number of elements in the underlying buffer. + #[inline(always)] + fn buffer_length(&self) -> usize { + self.get_buffer().len() + } + + /// Get if no bytes are available in the buffer. + /// + /// This operators on the underlying buffer: that is, + /// it returns if [`as_slice`] would return an empty slice. + /// + /// [`as_slice`]: Iter::as_slice + #[inline(always)] + fn is_buffer_empty(&self) -> bool { + self.cursor() >= self.get_buffer().len() + } + + /// Get the current index of the iterator in the slice. + fn cursor(&self) -> usize; + + /// Set the current index of the iterator in the slice. + /// + /// This is **NOT** the current position of the iterator, + /// since iterators may skip digits: this is the cursor + /// in the underlying buffer. For example, if `slc[2]` is + /// skipped, `set_cursor(3)` would be the 3rd element in + /// the iterator, not the 4th. + /// + /// # Safety + /// + /// Safe if `index <= self.buffer_length()`. Although this + /// won't affect safety, the caller also should be careful it + /// does not set the cursor within skipped characters + /// since this could affect correctness: an iterator that + /// only accepts non-consecutive digit separators would + /// pass if the cursor was set between the two. + unsafe fn set_cursor(&mut self, index: usize); + + /// Get the current number of digits returned by the iterator. + /// + /// For contiguous iterators, this can include the sign character, decimal + /// point, and the exponent sign (that is, it is always the cursor). For + /// non-contiguous iterators, this must always be the only the number of + /// digits returned. + /// + /// This is never used for indexing but will be used for API detection. + fn current_count(&self) -> usize; + + // PROPERTIES + + /// Determine if the buffer is contiguous. + #[inline(always)] + fn is_contiguous(&self) -> bool { + Self::IS_CONTIGUOUS + } + + /// Get the next value available without consuming it. + /// + /// This does **NOT** skip digits, and directly fetches the item + /// from the underlying buffer. + #[inline(always)] + fn first(&self) -> Option<&'a u8> { + self.get_buffer().get(self.cursor()) + } + + /// Check if the next element is a given value. + #[inline(always)] + fn first_is_cased(&self, value: u8) -> bool { + Some(&value) == self.first() + } + + /// Check if the next element is a given value without case sensitivity. + #[inline(always)] + fn first_is_uncased(&self, value: u8) -> bool { + if let Some(&c) = self.first() { + c.eq_ignore_ascii_case(&value) + } else { + false + } + } + + /// Check if the next item in buffer is a given value with optional case + /// sensitivity. + #[inline(always)] + fn first_is(&self, value: u8, is_cased: bool) -> bool { + if is_cased { + self.first_is_cased(value) + } else { + self.first_is_uncased(value) + } + } + + // STEP BY + // ------- + + /// Advance the internal slice by `N` elements. + /// + /// This does not advance the iterator by `N` elements for + /// non-contiguous iterators: this just advances the internal, + /// underlying buffer. This is useful for multi-digit optimizations + /// for contiguous iterators. + /// + /// This does not increment the count of items: returns: this only + /// increments the index, not the total digits returned. You must use + /// this carefully: if stepping over a digit, you must then call + /// [`increment_count`] afterwards or else the internal count will + /// be incorrect. + /// + /// [`increment_count`]: DigitsIter::increment_count + /// + /// # Panics + /// + /// This will panic if the buffer advances for non-contiguous + /// iterators if the current byte is a digit separator, or if the + /// count is more than 1. + /// + /// # Safety + /// + /// As long as the iterator is at least `N` elements, this + /// is safe. + unsafe fn step_by_unchecked(&mut self, count: usize); + + /// Advance the internal slice by 1 element. + /// + /// + /// This does not increment the count of items: returns: this only + /// increments the index, not the total digits returned. You must + /// use this carefully: if stepping over a digit, you must then call + /// [`increment_count`] afterwards or else the internal count will + /// be incorrect. + /// + /// [`increment_count`]: DigitsIter::increment_count + /// + /// # Panics + /// + /// This will panic if the buffer advances for non-contiguous + /// iterators if the current byte is a digit separator. + /// + /// # Safety + /// + /// Safe as long as the iterator is not empty. + #[inline(always)] + unsafe fn step_unchecked(&mut self) { + debug_assert!(!self.as_slice().is_empty()); + // SAFETY: safe if `self.index < self.buffer_length()`. + unsafe { self.step_by_unchecked(1) }; + } + + // READ + // ---- + + /// Read a value of a difference type from the iterator. + /// + /// This does **not** advance the internal state of the iterator. + /// This can only be implemented for contiguous iterators: non- + /// contiguous iterators **MUST** panic. + /// + /// # Panics + /// + /// If the iterator is a non-contiguous iterator. + /// + /// # Safety + /// + /// Safe as long as the number of the buffer is contains as least as + /// many bytes as the size of V. This must be unimplemented for + /// non-contiguous iterators. + #[inline(always)] + unsafe fn peek_many_unchecked(&self) -> V { + unimplemented!(); + } + + /// Try to read a the next four bytes as a u32. + /// + /// This does not advance the internal state of the iterator. + #[inline(always)] + fn peek_u32(&self) -> Option { + if Self::IS_CONTIGUOUS && self.as_slice().len() >= mem::size_of::() { + // SAFETY: safe since we've guaranteed the buffer is greater than + // the number of elements read. u32 is valid for all bit patterns + unsafe { Some(self.peek_many_unchecked()) } + } else { + None + } + } + + /// Try to read the next eight bytes as a u64. + /// + /// This does not advance the internal state of the iterator. + #[inline(always)] + fn peek_u64(&self) -> Option { + if Self::IS_CONTIGUOUS && self.as_slice().len() >= mem::size_of::() { + // SAFETY: safe since we've guaranteed the buffer is greater than + // the number of elements read. u64 is valid for all bit patterns + unsafe { Some(self.peek_many_unchecked()) } + } else { + None + } + } +} + +/// Iterator over a contiguous block of bytes. +/// +/// This allows us to convert to-and-from-slices, raw pointers, and +/// peek/query the data from either end cheaply. +/// +/// A default implementation is provided for slice iterators. +/// This trait **should never** return `null` from `as_ptr`, or be +/// implemented for non-contiguous data. +pub trait DigitsIter<'a>: Iterator + Iter<'a> { + /// Get if the iterator cannot return any more elements. + /// + /// This may advance the internal iterator state, but not + /// modify the next returned value. + /// + /// If this is an iterator, this is based on the number of items + /// left to be returned. We do not necessarly know the length of + /// the buffer. If this is a non-contiguous iterator, this **MUST** + /// advance the state until it knows a value can be returned. + /// + /// Any incorrect implementations of this affect all safety invariants + /// for the rest of the trait. For contiguous iterators, this can be + /// as simple as checking if `self.cursor >= self.slc.len()`, but for + /// non-contiguous iterators you **MUST** advance to the next element + /// to be returned, then check to see if a value exists. The safest + /// implementation is always to check if `self.peek().is_none()` and + /// ensure [`peek`] is always safe. + /// + /// If you would like to see if the cursor is at the end of the buffer, + /// see [`is_buffer_empty`] instead. + /// + /// [`is_buffer_empty`]: Iter::is_buffer_empty + /// [`peek`]: DigitsIter::peek + #[inline(always)] + #[allow(clippy::wrong_self_convention)] // reason="required for peeking next item" + fn is_consumed(&mut self) -> bool { + self.peek().is_none() + } + + /// Increment the number of digits that have been returned by the iterator. + /// + /// For contiguous iterators, this is a no-op. For non-contiguous iterators, + /// this increments the count by 1. + fn increment_count(&mut self); + + /// Peek the next value of the iterator, without consuming it. + /// + /// Note that this can modify the internal state, by skipping digits + /// for iterators that find the first non-zero value, etc. We optimize + /// this for the case where we have contiguous iterators, since + /// non-contiguous iterators already have a major performance penalty. + fn peek(&mut self) -> Option; + + /// Peek the next value of the iterator, and step only if it exists. + #[inline(always)] + fn try_read(&mut self) -> Option { + if let Some(value) = self.peek() { + // SAFETY: the slice cannot be empty because we peeked a value. + unsafe { self.step_unchecked() }; + Some(value) + } else { + None + } + } + + /// Check if the next element is a given value. + #[inline(always)] + fn peek_is_cased(&mut self, value: u8) -> bool { + Some(&value) == self.peek() + } + + /// Check if the next element is a given value without case sensitivity. + #[inline(always)] + fn peek_is_uncased(&mut self, value: u8) -> bool { + if let Some(&c) = self.peek() { + c.eq_ignore_ascii_case(&value) + } else { + false + } + } + + /// Check if the next element is a given value with optional case + /// sensitivity. + #[inline(always)] + fn peek_is(&mut self, value: u8, is_cased: bool) -> bool { + if is_cased { + self.peek_is_cased(value) + } else { + self.peek_is_uncased(value) + } + } + + /// Peek the next value and consume it if the read value matches the + /// expected one. + #[inline(always)] + fn read_if bool>(&mut self, pred: Pred) -> Option { + // NOTE: This was implemented to remove usage of unsafe throughout to code + // base, however, performance was really not up to scratch. I'm not sure + // the cause of this. + if let Some(&peeked) = self.peek() { + if pred(peeked) { + // SAFETY: the slice cannot be empty because we peeked a value. + unsafe { self.step_unchecked() }; + Some(peeked) + } else { + None + } + } else { + None + } + } + + /// Read a value if the value matches the provided one. + #[inline(always)] + fn read_if_value_cased(&mut self, value: u8) -> Option { + if self.peek() == Some(&value) { + // SAFETY: the slice cannot be empty because we peeked a value. + unsafe { self.step_unchecked() }; + Some(value) + } else { + None + } + } + + /// Read a value if the value matches the provided one without case + /// sensitivity. + #[inline(always)] + fn read_if_value_uncased(&mut self, value: u8) -> Option { + self.read_if(|x| x.eq_ignore_ascii_case(&value)) + } + + /// Read a value if the value matches the provided one. + #[inline(always)] + fn read_if_value(&mut self, value: u8, is_cased: bool) -> Option { + if is_cased { + self.read_if_value_cased(value) + } else { + self.read_if_value_uncased(value) + } + } + + /// Skip zeros from the start of the iterator + #[inline(always)] + fn skip_zeros(&mut self) -> usize { + let start = self.current_count(); + while self.read_if_value_cased(b'0').is_some() { + self.increment_count(); + } + self.current_count() - start + } + + /// Determine if the character is a digit. + fn is_digit(&self, value: u8) -> bool; +} diff --git a/rust/vendor/lexical-util/src/lib.rs b/rust/vendor/lexical-util/src/lib.rs new file mode 100644 index 0000000..7f2ccb5 --- /dev/null +++ b/rust/vendor/lexical-util/src/lib.rs @@ -0,0 +1,144 @@ +//! Shared utilities for lexical conversion routines. +//! +//! These are not meant to be used publicly for any numeric +//! conversion routines, but provide optimized math routines, +//! format packed struct definitions, and custom iterators +//! for all workspaces. +//! +//! # Features +//! +//! * `power-of-two` - Add support for parsing and writing power-of-two integer +//! strings. +//! * `radix` - Add support for parsing and writing strings of any radix. +//! * `format` - Add support for custom number formats. +//! * `write-integers` - Add support for writing integers (used for +//! [`lexical-write-integer`]). +//! * `write-floats` - Add support for writing floats (used for +//! [`lexical-write-float`]). +//! * `parse-integers` - Add support for parsing integers (used for +//! [`lexical-parse-integer`]). +//! * `parse-floats` - Add support for parsing floats (used for +//! [`lexical-write-float`]). +//! * `compact` - Reduce code size at the cost of performance. +//! * `f16` - Enable support for half-precision [`f16`][`ieee-f16`] and +//! [`bf16`][`brain-float`] floats. +//! * `std` (Default) - Disable to allow use in a [`no_std`] environment. +//! +//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html +//! [`ieee-f16`]: https://en.wikipedia.org/wiki/Half-precision_floating-point_format +//! [`brain-float`]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format +//! +//! # Public API +//! +//! [`lexical-util`] mainly exists as an implementation detail for +//! the other lexical crates, although its API is mostly stable. If you would +//! like to use a high-level API that writes to and parses from [`String`] and +//! [`str`], respectively, please look at [`lexical`] instead. If you would like +//! an API that supports multiple numeric conversions without a dependency on +//! [`alloc`], please look at [`lexical-core`] instead. +//! +//!
+//! +//! Any undocumented, implementation details may change release-to-release +//! without major or minor version changes. Use internal implementation details +//! at your own risk. Any changes other than to [`NumberFormatBuilder`], +//! [`NumberFormat`], [`mod@format`], and [`mod@options`] will not be considered +//! a breaking change. +//! +//!
+//! +//! # Version Support +//! +//! The minimum, standard, required version is [`1.63.0`][`rust-1.63.0`], for +//! const generic support. Older versions of lexical support older Rust +//! versions. +//! +//! # Safety Guarantees +//! +//! For a detailed breakdown on the use of [`unsafe`], how and why our traits +//! are implemented safely, and how to verify this, see [`Safety`]. +//! +//! [`lexical`]: https://crates.io/crates/lexical +//! [`lexical-parse-float`]: https://crates.io/crates/lexical-parse-float +//! [`lexical-parse-integer`]: https://crates.io/crates/lexical-parse-integer +//! [`lexical-write-float`]: https://crates.io/crates/lexical-write-float +//! [`lexical-write-integer`]: https://crates.io/crates/lexical-write-integer +//! [`lexical-core`]: https://crates.io/crates/lexical-core +//! [`lexical-util`]: https://crates.io/crates/lexical-util +//! [`rust-1.63.0`]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html +//! [`alloc`]: https://doc.rust-lang.org/alloc/ +//! [`String`]: https://doc.rust-lang.org/alloc/string/struct.String.html +//! [`Safety`]: https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-util/docs/Safety.md +//! [`unsafe`]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html + +// FIXME: Implement clippy/allow reasons once we drop support for 1.80.0 and below +// Clippy reasons were stabilized in 1.81.0. + +// We want to have the same safety guarantees as Rust core, +// so we allow unused unsafe to clearly document safety guarantees. +#![allow(unused_unsafe)] +#![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![deny( + clippy::doc_markdown, + clippy::unnecessary_safety_comment, + clippy::semicolon_if_nothing_returned, + clippy::unwrap_used, + clippy::as_underscore +)] +#![allow( + // used when concepts are logically separate + clippy::match_same_arms, + // loss of precision is intentional + clippy::integer_division, + // mathematical names use 1-character identifiers + clippy::min_ident_chars, + // these are not cryptographically secure contexts + clippy::integer_division_remainder_used, + // this can be intentional + clippy::module_name_repetitions, + // this is intentional: already passing a pointer and need performance + clippy::needless_pass_by_value, + // we use this for inline formatting for unsafe blocks + clippy::semicolon_inside_block, +)] + +pub mod algorithm; +pub mod ascii; +pub mod assert; +pub mod bf16; +pub mod constants; +pub mod digit; +pub mod div128; +pub mod error; +pub mod extended_float; +pub mod f16; +pub mod format; +pub mod iterator; +pub mod mul; +pub mod num; +pub mod options; +pub mod result; +pub mod step; + +mod api; +mod feature_format; +mod format_builder; +mod format_flags; +mod libm; +mod noskip; +mod not_feature_format; +mod prebuilt_formats; +mod skip; + +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +pub use constants::{FormattedSize, BUFFER_SIZE}; +pub use error::Error; +pub use format::{NumberFormat, NumberFormatBuilder}; +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] +pub use options::ParseOptions; +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +pub use options::WriteOptions; +pub use result::Result; diff --git a/rust/vendor/lexical-util/src/libm.rs b/rust/vendor/lexical-util/src/libm.rs new file mode 100644 index 0000000..da9413b --- /dev/null +++ b/rust/vendor/lexical-util/src/libm.rs @@ -0,0 +1,269 @@ +//! Float helpers for a `no_std` environment. +//! +//! These are adapted from libm, a port of musl libc's libm to Rust. +//! libm can be found online [here](https://github.com/rust-lang/libm), +//! and is similarly licensed under an Apache2.0/MIT license + +#![cfg(all(not(feature = "std"), any(feature = "parse-floats", feature = "write-floats")))] +#![cfg_attr(any(), rustfmt::skip)] + +/// # Safety +/// +/// Safe as long as `e` is properly initialized. +macro_rules! volatile { +($e:expr) => { + // SAFETY: safe as long as `$e` has been properly initialized. + unsafe { + core::ptr::read_volatile(&$e); + } +}; +} + +/// Floor (f64) +/// +/// Finds the nearest integer less than or equal to `x`. +pub(crate) fn floord(x: f64) -> f64 { + const TOINT: f64 = 1. / f64::EPSILON; + + let ui = x.to_bits(); + let e = ((ui >> 52) & 0x7ff) as i32; + + if (e >= 0x3ff + 52) || (x == 0.) { + return x; + } + /* y = int(x) - x, where int(x) is an integer neighbor of x */ + let y = if (ui >> 63) != 0 { + x - TOINT + TOINT - x + } else { + x + TOINT - TOINT - x + }; + /* special case because of non-nearest rounding modes */ + if e < 0x3ff { + volatile!(y); + return if (ui >> 63) != 0 { + -1. + } else { + 0. + }; + } + if y > 0. { + x + y - 1. + } else { + x + y + } +} + +/// Floor (f32) +/// +/// Finds the nearest integer less than or equal to `x`. +pub(crate) fn floorf(x: f32) -> f32 { + let mut ui = x.to_bits(); + let e = (((ui >> 23) as i32) & 0xff) - 0x7f; + + if e >= 23 { + return x; + } + if e >= 0 { + let m: u32 = 0x007fffff >> e; + if (ui & m) == 0 { + return x; + } + volatile!(x + f32::from_bits(0x7b800000)); + if ui >> 31 != 0 { + ui += m; + } + ui &= !m; + } else { + volatile!(x + f32::from_bits(0x7b800000)); + if ui >> 31 == 0 { + ui = 0; + } else if ui << 1 != 0 { + return -1.0; + } + } + f32::from_bits(ui) +} + +/* origin: FreeBSD /usr/src/lib/msun/src/e_log.c */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ +/* log(x) + * Return the logarithm of x + * + * Method : + * 1. Argument Reduction: find k and f such that + * x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * 2. Approximation of log(1+f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Remez algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s + * (the values of Lg1 to Lg7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lg1*s +...+Lg7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log(1+f) = f - s*(f - R) (if f is not too large) + * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) + * + * 3. Finally, log(x) = k*ln2 + log(1+f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log(x) is NaN with signal if x < 0 (including -INF) ; + * log(+INF) is +INF; log(0) is -INF with signal; + * log(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#[allow(clippy::eq_op, clippy::excessive_precision)] // reason="values need to be exact under all conditions" +pub(crate) fn logd(mut x: f64) -> f64 { + const LN2_HI: f64 = 6.93147180369123816490e-01; /* 3fe62e42 fee00000 */ + const LN2_LO: f64 = 1.90821492927058770002e-10; /* 3dea39ef 35793c76 */ + const LG1: f64 = 6.666666666666735130e-01; /* 3FE55555 55555593 */ + const LG2: f64 = 3.999999999940941908e-01; /* 3FD99999 9997FA04 */ + const LG3: f64 = 2.857142874366239149e-01; /* 3FD24924 94229359 */ + const LG4: f64 = 2.222219843214978396e-01; /* 3FCC71C5 1D8E78AF */ + const LG5: f64 = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */ + const LG6: f64 = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */ + const LG7: f64 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + + let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 + + let mut ui = x.to_bits(); + let mut hx: u32 = (ui >> 32) as u32; + let mut k: i32 = 0; + + if (hx < 0x00100000) || ((hx >> 31) != 0) { + /* x < 2**-126 */ + if ui << 1 == 0 { + return -1. / (x * x); /* log(+-0)=-inf */ + } + if hx >> 31 != 0 { + return (x - x) / 0.0; /* log(-#) = NaN */ + } + /* subnormal number, scale x up */ + k -= 54; + x *= x1p54; + ui = x.to_bits(); + hx = (ui >> 32) as u32; + } else if hx >= 0x7ff00000 { + return x; + } else if hx == 0x3ff00000 && ui << 32 == 0 { + return 0.; + } + + /* reduce x into [sqrt(2)/2, sqrt(2)] */ + hx += 0x3ff00000 - 0x3fe6a09e; + k += ((hx >> 20) as i32) - 0x3ff; + hx = (hx & 0x000fffff) + 0x3fe6a09e; + ui = ((hx as u64) << 32) | (ui & 0xffffffff); + x = f64::from_bits(ui); + + let f: f64 = x - 1.0; + let hfsq: f64 = 0.5 * f * f; + let s: f64 = f / (2.0 + f); + let z: f64 = s * s; + let w: f64 = z * z; + let t1: f64 = w * (LG2 + w * (LG4 + w * LG6)); + let t2: f64 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7))); + let r: f64 = t2 + t1; + let dk: f64 = k as f64; + s * (hfsq + r) + dk * LN2_LO - hfsq + f + dk * LN2_HI +} + +/* origin: FreeBSD /usr/src/lib/msun/src/e_logf.c */ +/* + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#[allow(clippy::eq_op, clippy::excessive_precision)] // reason="values need to be exact under all conditions" +pub(crate) fn logf(mut x: f32) -> f32 { + const LN2_HI: f32 = 6.9313812256e-01; /* 0x3f317180 */ + const LN2_LO: f32 = 9.0580006145e-06; /* 0x3717f7d1 */ + /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ + const LG1: f32 = 0.66666662693; /* 0xaaaaaa.0p-24 */ + const LG2: f32 = 0.40000972152; /* 0xccce13.0p-25 */ + const LG3: f32 = 0.28498786688; /* 0x91e9ee.0p-25 */ + const LG4: f32 = 0.24279078841; /* 0xf89e26.0p-26 */ + + let x1p25 = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 + + let mut ix = x.to_bits(); + let mut k = 0i32; + + if (ix < 0x00800000) || ((ix >> 31) != 0) { + /* x < 2**-126 */ + if ix << 1 == 0 { + return -1. / (x * x); /* log(+-0)=-inf */ + } + if (ix >> 31) != 0 { + return (x - x) / 0.; /* log(-#) = NaN */ + } + /* subnormal number, scale up x */ + k -= 25; + x *= x1p25; + ix = x.to_bits(); + } else if ix >= 0x7f800000 { + return x; + } else if ix == 0x3f800000 { + return 0.; + } + + /* reduce x into [sqrt(2)/2, sqrt(2)] */ + ix += 0x3f800000 - 0x3f3504f3; + k += ((ix >> 23) as i32) - 0x7f; + ix = (ix & 0x007fffff) + 0x3f3504f3; + x = f32::from_bits(ix); + + let f = x - 1.; + let s = f / (2. + f); + let z = s * s; + let w = z * z; + let t1 = w * (LG2 + w * LG4); + let t2 = z * (LG1 + w * LG3); + let r = t2 + t1; + let hfsq = 0.5 * f * f; + let dk = k as f32; + s * (hfsq + r) + dk * LN2_LO - hfsq + f + dk * LN2_HI +} diff --git a/rust/vendor/lexical-util/src/mul.rs b/rust/vendor/lexical-util/src/mul.rs new file mode 100644 index 0000000..1ea9e92 --- /dev/null +++ b/rust/vendor/lexical-util/src/mul.rs @@ -0,0 +1,58 @@ +//! Fast multiplication routines. + +use crate::num::{as_cast, UnsignedInteger}; + +/// Multiply two unsigned, integral values, and return the high and low product. +/// +/// The high product is the upper half of the product and the low product is the +/// lower half. The `full` type is the full type size, while the `half` type is +/// the type with exactly half the bits. +#[inline(always)] +pub fn mul(x: Full, y: Full) -> (Full, Full) +where + Full: UnsignedInteger, + Half: UnsignedInteger, +{ + // Extract high-and-low masks. + let x1 = x >> Half::BITS as i32; + let x0 = x & as_cast(Half::MAX); + let y1 = y >> Half::BITS as i32; + let y0 = y & as_cast(Half::MAX); + + let w0 = x0 * y0; + let tmp = (x1 * y0) + (w0 >> Half::BITS as i32); + let w1 = tmp & as_cast(Half::MAX); + let w2 = tmp >> Half::BITS as i32; + let w1 = w1 + x0 * y1; + let hi = (x1 * y1) + w2 + (w1 >> Half::BITS as i32); + let lo = x.wrapping_mul(y); + + (hi, lo) +} + +/// Multiply two unsigned, integral values, and return the high product. +/// +/// The high product is the upper half of the product. The `full` type is the +/// full type size, while the `half` type is the type with exactly half the +/// bits. +#[inline(always)] +pub fn mulhi(x: Full, y: Full) -> Full +where + Full: UnsignedInteger, + Half: UnsignedInteger, +{ + // Extract high-and-low masks. + let x1 = x >> Half::BITS as i32; + let x0 = x & as_cast(Half::MAX); + let y1 = y >> Half::BITS as i32; + let y0 = y & as_cast(Half::MAX); + + let w0 = x0 * y0; + let m = (x0 * y1) + (w0 >> Half::BITS as i32); + let w1 = m & as_cast(Half::MAX); + let w2 = m >> Half::BITS as i32; + + let w3 = (x1 * y0 + w1) >> Half::BITS as i32; + + x1 * y1 + w2 + w3 +} diff --git a/rust/vendor/lexical-util/src/noskip.rs b/rust/vendor/lexical-util/src/noskip.rs new file mode 100644 index 0000000..5852ba7 --- /dev/null +++ b/rust/vendor/lexical-util/src/noskip.rs @@ -0,0 +1,280 @@ +//! An iterator over a slice. +//! +//! This iterator has both the length of the original slice, as +//! well as the current position of the iterator in the buffer. + +#![cfg(all(any(feature = "parse-floats", feature = "parse-integers"), not(feature = "format")))] + +use core::{mem, ptr}; + +use crate::digit::char_is_digit_const; +use crate::format::NumberFormat; +use crate::iterator::{DigitsIter, Iter}; + +// AS DIGITS +// --------- + +/// Trait to simplify creation of a `Bytes` object. +pub trait AsBytes<'a> { + /// Create `Bytes` from object. + fn bytes(&'a self) -> Bytes<'a, __>; +} + +impl<'a> AsBytes<'a> for [u8] { + #[inline(always)] + fn bytes(&'a self) -> Bytes<'a, __> { + Bytes::new(self) + } +} + +// DIGITS +// ------ + +/// Slice iterator that stores the original length of the slice. +#[derive(Clone)] +pub struct Bytes<'a, const __: u128> { + /// The raw slice for the iterator. + slc: &'a [u8], + /// Current index of the iterator in the slice. + index: usize, +} + +impl<'a, const __: u128> Bytes<'a, __> { + /// Create new byte object. + #[inline(always)] + pub const fn new(slc: &'a [u8]) -> Self { + Self { + slc, + index: 0, + } + } + + /// Initialize the slice from raw parts. + /// + /// # Safety + /// + /// This is safe if and only if the index is <= `slc.len()`. + /// For this reason, since it's easy to get wrong, we only + /// expose it to `DigitsIterator` and nothing else. + #[inline(always)] + #[allow(clippy::assertions_on_constants)] // reason="ensuring safety invariants are valid" + const unsafe fn from_parts(slc: &'a [u8], index: usize) -> Self { + debug_assert!(index <= slc.len()); + debug_assert!(Self::IS_CONTIGUOUS); + Self { + slc, + index, + } + } + + /// Get iterator over integer digits. + #[inline(always)] + pub fn integer_iter<'b>(&'b mut self) -> DigitsIterator<'a, 'b, __> { + DigitsIterator { + byte: self, + } + } + + /// Get iterator over fraction digits. + #[inline(always)] + pub fn fraction_iter<'b>(&'b mut self) -> DigitsIterator<'a, 'b, __> { + DigitsIterator { + byte: self, + } + } + + /// Get iterator over exponent digits. + #[inline(always)] + pub fn exponent_iter<'b>(&'b mut self) -> DigitsIterator<'a, 'b, __> { + DigitsIterator { + byte: self, + } + } + + /// Get iterator over special floating point values. + #[inline(always)] + pub fn special_iter<'b>(&'b mut self) -> DigitsIterator<'a, 'b, __> { + DigitsIterator { + byte: self, + } + } +} + +unsafe impl<'a, const __: u128> Iter<'a> for Bytes<'a, __> { + const IS_CONTIGUOUS: bool = true; + + #[inline(always)] + fn get_buffer(&self) -> &'a [u8] { + self.slc + } + + /// Get the current index of the iterator in the slice. + #[inline(always)] + fn cursor(&self) -> usize { + self.index + } + + /// Set the current index of the iterator in the slice. + /// + /// # Safety + /// + /// Safe if `index <= self.buffer_length()`. + #[inline(always)] + unsafe fn set_cursor(&mut self, index: usize) { + debug_assert!(index <= self.buffer_length()); + self.index = index; + } + + /// Get the current number of digits returned by the iterator. + /// + /// For contiguous iterators, this can include the sign character, decimal + /// point, and the exponent sign (that is, it is always the cursor). For + /// non-contiguous iterators, this must always be the only the number of + /// digits returned. + #[inline(always)] + fn current_count(&self) -> usize { + self.index + } + + #[inline(always)] + #[allow(clippy::assertions_on_constants)] // reason="ensuring safety invariants are valid" + unsafe fn step_by_unchecked(&mut self, count: usize) { + assert!(Self::IS_CONTIGUOUS); + debug_assert!(self.as_slice().len() >= count); + self.index += count; + } + + #[inline(always)] + #[allow(clippy::assertions_on_constants)] // reason="ensuring safety invariants are valid" + unsafe fn peek_many_unchecked(&self) -> V { + debug_assert!(Self::IS_CONTIGUOUS); + debug_assert!(self.as_slice().len() >= mem::size_of::()); + + // SAFETY: safe as long as the slice has at least count elements. + unsafe { ptr::read_unaligned::(self.as_ptr() as *const _) } + } +} + +// DIGITS ITERATOR +// --------------- + +/// Slice iterator that stores the original length of the slice. +pub struct DigitsIterator<'a: 'b, 'b, const __: u128> { + /// The internal byte object for the no-skip iterator. + byte: &'b mut Bytes<'a, __>, +} + +impl<'a: 'b, 'b, const __: u128> DigitsIterator<'a, 'b, __> { + /// Create a new digits iterator from the bytes underlying item. + #[inline(always)] + pub fn new(byte: &'b mut Bytes<'a, __>) -> Self { + Self { + byte, + } + } + + /// Take the first N digits from the iterator. + /// + /// This only takes the digits if we have a contiguous iterator. + /// It takes the digits, validating the bounds, and then advanced + /// the iterators state. + #[cfg_attr(not(feature = "compact"), inline(always))] + #[allow(clippy::assertions_on_constants)] // reason="ensuring safety invariants are valid" + pub fn take_n(&mut self, n: usize) -> Option> { + debug_assert!(Self::IS_CONTIGUOUS); + let end = self.byte.slc.len().min(n + self.cursor()); + // NOTE: The compiler should be able to optimize this out. + let slc: &[u8] = &self.byte.slc[..end]; + + // SAFETY: Safe since we just ensured the underlying slice has that count + // elements, so both the underlying slice for this and this **MUST** + // have at least count elements. We do static checking on the bounds for this. + unsafe { + let byte: Bytes<'_, __> = Bytes::from_parts(slc, self.cursor()); + unsafe { self.set_cursor(end) }; + Some(byte) + } + } +} + +unsafe impl<'a: 'b, 'b, const __: u128> Iter<'a> for DigitsIterator<'a, 'b, __> { + const IS_CONTIGUOUS: bool = Bytes::<'a, __>::IS_CONTIGUOUS; + + #[inline(always)] + fn get_buffer(&self) -> &'a [u8] { + self.byte.get_buffer() + } + + #[inline(always)] + fn cursor(&self) -> usize { + self.byte.cursor() + } + + #[inline(always)] + unsafe fn set_cursor(&mut self, index: usize) { + debug_assert!(index <= self.buffer_length()); + // SAFETY: safe if `index <= self.buffer_length()`. + unsafe { self.byte.set_cursor(index) }; + } + + #[inline(always)] + fn current_count(&self) -> usize { + self.byte.current_count() + } + + #[inline(always)] + unsafe fn step_by_unchecked(&mut self, count: usize) { + debug_assert!(self.as_slice().len() >= count); + // SAFETY: safe as long as `slc.len() >= count`. + unsafe { self.byte.step_by_unchecked(count) } + } + + #[inline(always)] + unsafe fn peek_many_unchecked(&self) -> V { + debug_assert!(self.as_slice().len() >= mem::size_of::()); + // SAFETY: safe as long as the slice has at least count elements. + unsafe { self.byte.peek_many_unchecked() } + } +} + +impl<'a: 'b, 'b, const FORMAT: u128> DigitsIter<'a> for DigitsIterator<'a, 'b, FORMAT> { + #[inline(always)] + fn is_consumed(&mut self) -> bool { + self.is_buffer_empty() + } + + // Always a no-op + #[inline(always)] + fn increment_count(&mut self) { + } + + #[inline(always)] + fn peek(&mut self) -> Option<::Item> { + self.byte.slc.get(self.byte.index) + } + + /// Determine if the character is a digit. + #[inline(always)] + fn is_digit(&self, value: u8) -> bool { + let format = NumberFormat::<{ FORMAT }> {}; + char_is_digit_const(value, format.mantissa_radix()) + } +} + +impl<'a: 'b, 'b, const __: u128> Iterator for DigitsIterator<'a, 'b, __> { + type Item = &'a u8; + + #[inline(always)] + fn next(&mut self) -> Option { + let value = self.byte.slc.get(self.byte.index)?; + self.byte.index += 1; + Some(value) + } +} + +impl<'a: 'b, 'b, const __: u128> ExactSizeIterator for DigitsIterator<'a, 'b, __> { + #[inline(always)] + fn len(&self) -> usize { + self.buffer_length() - self.cursor() + } +} diff --git a/rust/vendor/lexical-util/src/not_feature_format.rs b/rust/vendor/lexical-util/src/not_feature_format.rs new file mode 100644 index 0000000..f7441bb --- /dev/null +++ b/rust/vendor/lexical-util/src/not_feature_format.rs @@ -0,0 +1,1433 @@ +//! Bare bones implementation of the format packed struct without feature +//! [`format`][crate#features]. +//! +//! See `feature_format` for detailed documentation. + +#![cfg(not(feature = "format"))] + +use crate::error::Error; +use crate::format_builder::NumberFormatBuilder; +use crate::format_flags as flags; + +/// Helper to access features from the packed format struct. +/// +/// Some of the core functionality includes support for: +/// - Digit separators: ignored characters used to make numbers more readable, +/// such as `100,000`. +/// - Non-decimal radixes: writing or parsing numbers written in binary, +/// hexadecimal, or other bases. +/// - Special numbers: disabling support for special floating-point, such as +/// [`NaN`][f64::NAN]. +/// - Number components: require signs, significant digits, and more. +/// +/// The following values are explicitly set, and therefore not configurable: +/// +/// 1. [`required_integer_digits`][NumberFormat::required_integer_digits] +/// 2. [`required_fraction_digits`][NumberFormat::required_fraction_digits] +/// 3. [`required_exponent_digits`][NumberFormat::required_exponent_digits] +/// 4. [`required_mantissa_digits`][NumberFormat::required_mantissa_digits] +/// 5. [`required_digits`][NumberFormat::required_digits] +/// 6. [`no_positive_mantissa_sign`][NumberFormat::no_positive_mantissa_sign] +/// 7. [`required_mantissa_sign`][NumberFormat::required_mantissa_sign] +/// 8. [`no_exponent_notation`][NumberFormat::no_exponent_notation] +/// 9. [`no_positive_exponent_sign`][NumberFormat::no_positive_exponent_sign] +/// 10. [`required_exponent_sign`][NumberFormat::required_exponent_sign] +/// 11. [`no_exponent_without_fraction`][NumberFormat::no_exponent_without_fraction] +/// 12. [`no_special`][NumberFormat::no_special] +/// 13. [`case_sensitive_special`][NumberFormat::case_sensitive_special] +/// 14. [`no_integer_leading_zeros`][NumberFormat::no_integer_leading_zeros] +/// 15. [`no_float_leading_zeros`][NumberFormat::no_float_leading_zeros] +/// 16. [`required_exponent_notation`][NumberFormat::required_exponent_notation] +/// 17. [`case_sensitive_exponent`][NumberFormat::case_sensitive_exponent] +/// 18. [`case_sensitive_base_prefix`][NumberFormat::case_sensitive_base_prefix] +/// 19. [`case_sensitive_base_suffix`][NumberFormat::case_sensitive_base_suffix] +/// 20. [`integer_internal_digit_separator`][NumberFormat::integer_internal_digit_separator] +/// 21. [`fraction_internal_digit_separator`][NumberFormat::fraction_internal_digit_separator] +/// 22. [`exponent_internal_digit_separator`][NumberFormat::exponent_internal_digit_separator] +/// 23. [`internal_digit_separator`][NumberFormat::internal_digit_separator] +/// 24. [`integer_leading_digit_separator`][NumberFormat::integer_leading_digit_separator] +/// 25. [`fraction_leading_digit_separator`][NumberFormat::fraction_leading_digit_separator] +/// 26. [`exponent_leading_digit_separator`][NumberFormat::exponent_leading_digit_separator] +/// 27. [`leading_digit_separator`][NumberFormat::leading_digit_separator] +/// 28. [`integer_trailing_digit_separator`][NumberFormat::integer_trailing_digit_separator] +/// 29. [`fraction_trailing_digit_separator`][NumberFormat::fraction_trailing_digit_separator] +/// 30. [`exponent_trailing_digit_separator`][NumberFormat::exponent_trailing_digit_separator] +/// 31. [`trailing_digit_separator`][NumberFormat::trailing_digit_separator] +/// 32. [`integer_consecutive_digit_separator`][NumberFormat::integer_consecutive_digit_separator] +/// 33. [`fraction_consecutive_digit_separator`][NumberFormat::fraction_consecutive_digit_separator] +/// 34. [`exponent_consecutive_digit_separator`][NumberFormat::exponent_consecutive_digit_separator] +/// 35. [`consecutive_digit_separator`][NumberFormat::consecutive_digit_separator] +/// 36. [`special_digit_separator`][NumberFormat::special_digit_separator] +/// 37. [`digit_separator`][NumberFormat::digit_separator] +/// 38. [`base_prefix`][NumberFormat::base_prefix] +/// 39. [`base_suffix`][NumberFormat::base_suffix] +/// 40. [`exponent_base`][NumberFormat::exponent_base] +/// 41. [`exponent_radix`][NumberFormat::exponent_radix] +/// +/// This should always be constructed via [`NumberFormatBuilder`]. +/// See [`NumberFormatBuilder`] for the fields for the packed struct. +pub struct NumberFormat; + +impl NumberFormat { + // CONSTRUCTORS + + /// Create new instance (for methods and validation). + /// + /// This uses the same settings as in the `FORMAT` packed struct. + #[inline(always)] + pub const fn new() -> Self { + Self {} + } + + // VALIDATION + + /// Determine if the number format is valid. + #[inline(always)] + pub const fn is_valid(&self) -> bool { + self.error().is_success() + } + + /// Get the error type from the format. + #[inline(always)] + pub const fn error(&self) -> Error { + format_error_impl(FORMAT) + } + + /// Determine if the radixes in the number format are valid. + #[inline(always)] + pub const fn is_valid_radix(&self) -> bool { + self.error_radix().is_success() + } + + /// Get the error type from the radix-only for the format. + /// + /// If [`Error::Success`] is returned, then no error occurred. + #[inline(always)] + pub const fn error_radix(&self) -> Error { + radix_error_impl(FORMAT) + } + + // NON-DIGIT SEPARATOR FLAGS & MASKS + + /// If digits are required before the decimal point. + /// + /// See [`required_integer_digits`][Self::required_integer_digits]. + pub const REQUIRED_INTEGER_DIGITS: bool = false; + + /// Get if digits are required before the decimal point. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `0.1` | ✔️ | + /// | `1` | ✔️ | + /// | `.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn required_integer_digits(&self) -> bool { + Self::REQUIRED_INTEGER_DIGITS + } + + /// If digits are required after the decimal point. + /// + /// See [`required_fraction_digits`][Self::required_fraction_digits]. + pub const REQUIRED_FRACTION_DIGITS: bool = false; + + /// Get if digits are required after the decimal point. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1` | ✔️ | + /// | `1.` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn required_fraction_digits(&self) -> bool { + Self::REQUIRED_FRACTION_DIGITS + } + + /// If digits are required after the exponent character. + /// + /// See [`required_exponent_digits`][Self::required_exponent_digits]. + pub const REQUIRED_EXPONENT_DIGITS: bool = true; + + /// Get if digits are required after the exponent character. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e+3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// | `1.1e+` | ❌ | + /// | `1.1e` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn required_exponent_digits(&self) -> bool { + Self::REQUIRED_EXPONENT_DIGITS + } + + /// If significant digits are required. + /// + /// See [`required_mantissa_digits`][Self::required_mantissa_digits]. + pub const REQUIRED_MANTISSA_DIGITS: bool = true; + + /// Get if at least 1 significant digit is required. + /// + /// If not required, then values like `.` (`0`) are valid, but empty strings + /// are still invalid. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `.` | ✔️ | + /// | `e10` | ✔️ | + /// | `.e10` | ✔️ | + /// | `` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn required_mantissa_digits(&self) -> bool { + Self::REQUIRED_MANTISSA_DIGITS + } + + /// If at least 1 digit in the number is required. + /// + /// See [`required_digits`][Self::required_digits]. + pub const REQUIRED_DIGITS: bool = true; + + /// Get if at least 1 digit in the number is required. + /// + /// This requires either [`mantissa`] or [`exponent`] digits. + /// + /// [`mantissa`]: Self::required_mantissa_digits + /// [`exponent`]: Self::required_exponent_digits + #[inline(always)] + pub const fn required_digits(&self) -> bool { + Self::REQUIRED_DIGITS + } + + /// If a positive sign before the mantissa is not allowed. + /// + /// See [`no_positive_mantissa_sign`][Self::no_positive_mantissa_sign]. + pub const NO_POSITIVE_MANTISSA_SIGN: bool = false; + + /// Get if a positive sign before the mantissa is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + #[inline(always)] + pub const fn no_positive_mantissa_sign(&self) -> bool { + Self::NO_POSITIVE_MANTISSA_SIGN + } + + /// If a sign symbol before the mantissa is required. + /// + /// See [`required_mantissa_sign`][Self::required_mantissa_sign]. + pub const REQUIRED_MANTISSA_SIGN: bool = false; + + /// Get if a sign symbol before the mantissa is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ❌ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + #[inline(always)] + pub const fn required_mantissa_sign(&self) -> bool { + Self::REQUIRED_MANTISSA_SIGN + } + + /// If exponent notation is not allowed. + /// + /// See [`no_exponent_notation`][Self::no_exponent_notation]. + pub const NO_EXPONENT_NOTATION: bool = false; + + /// Get if exponent notation is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1.1` | ✔️ | + /// | `1.1e` | ❌ | + /// | `1.1e5` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn no_exponent_notation(&self) -> bool { + Self::NO_EXPONENT_NOTATION + } + + /// If a positive sign before the exponent is not allowed. + /// + /// See [`no_positive_exponent_sign`][Self::no_positive_exponent_sign]. + pub const NO_POSITIVE_EXPONENT_SIGN: bool = false; + + /// Get if a positive sign before the exponent is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ✔️ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn no_positive_exponent_sign(&self) -> bool { + Self::NO_POSITIVE_EXPONENT_SIGN + } + + /// If a sign symbol before the exponent is required. + /// + /// See [`required_exponent_sign`][Self::required_exponent_sign]. + pub const REQUIRED_EXPONENT_SIGN: bool = false; + + /// Get if a sign symbol before the exponent is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ❌ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn required_exponent_sign(&self) -> bool { + Self::REQUIRED_EXPONENT_SIGN + } + + /// If an exponent without fraction is not allowed. + /// + /// See [`no_exponent_without_fraction`][Self::no_exponent_without_fraction]. + pub const NO_EXPONENT_WITHOUT_FRACTION: bool = false; + + /// Get if an exponent without fraction is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1e3` | ❌ | + /// | `1.e3` | ❌ | + /// | `1.1e` | ✔️ | + /// | `.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn no_exponent_without_fraction(&self) -> bool { + Self::NO_EXPONENT_WITHOUT_FRACTION + } + + /// If special (non-finite) values are not allowed. + /// + /// See [`no_special`][Self::no_special]. + pub const NO_SPECIAL: bool = false; + + /// Get if special (non-finite) values are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `NaN` | ❌ | + /// | `inf` | ❌ | + /// | `-Infinity` | ❌ | + /// | `1.1e` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn no_special(&self) -> bool { + Self::NO_SPECIAL + } + + /// If special (non-finite) values are case-sensitive. + /// + /// See [`case_sensitive_special`][Self::case_sensitive_special]. + pub const CASE_SENSITIVE_SPECIAL: bool = false; + + /// Get if special (non-finite) values are case-sensitive. + /// + /// If set to [`true`], then `NaN` and `nan` are treated as the same value + /// ([Not a Number][f64::NAN]). Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn case_sensitive_special(&self) -> bool { + Self::CASE_SENSITIVE_SPECIAL + } + + /// If leading zeros before an integer are not allowed. + /// + /// See [`no_integer_leading_zeros`][Self::no_integer_leading_zeros]. + pub const NO_INTEGER_LEADING_ZEROS: bool = false; + + /// Get if leading zeros before an integer are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// + /// # Used For + /// + /// - Parse Integer + #[inline(always)] + pub const fn no_integer_leading_zeros(&self) -> bool { + Self::NO_INTEGER_LEADING_ZEROS + } + + /// If leading zeros before a float are not allowed. + /// + /// See [`no_float_leading_zeros`][Self::no_float_leading_zeros]. + pub const NO_FLOAT_LEADING_ZEROS: bool = false; + + /// Get if leading zeros before a float are not allowed. + /// + /// This is before the significant digits of the float, that is, if there is + /// 1 or more digits in the integral component and the leading digit is 0, + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `01.0` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// | `0.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn no_float_leading_zeros(&self) -> bool { + Self::NO_FLOAT_LEADING_ZEROS + } + + /// If exponent notation is required. + /// + /// See [`required_exponent_notation`][Self::required_exponent_notation]. + pub const REQUIRED_EXPONENT_NOTATION: bool = false; + + /// Get if exponent notation is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ❌ | + /// | `1.0` | ❌ | + /// | `1e3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float + #[inline(always)] + pub const fn required_exponent_notation(&self) -> bool { + Self::REQUIRED_EXPONENT_NOTATION + } + + /// If exponent characters are case-sensitive. + /// + /// See [`case_sensitive_exponent`][Self::case_sensitive_exponent]. + pub const CASE_SENSITIVE_EXPONENT: bool = false; + + /// Get if exponent characters are case-sensitive. + /// + /// If set to [`true`], then the exponent character `e` would be considered + /// the different from `E`. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn case_sensitive_exponent(&self) -> bool { + Self::CASE_SENSITIVE_EXPONENT + } + + /// If base prefixes are case-sensitive. + /// + /// See [`case_sensitive_base_prefix`][Self::case_sensitive_base_prefix]. + pub const CASE_SENSITIVE_BASE_PREFIX: bool = false; + + /// Get if base prefixes are case-sensitive. + /// + /// If set to [`true`], then the base prefix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn case_sensitive_base_prefix(&self) -> bool { + Self::CASE_SENSITIVE_BASE_PREFIX + } + + /// If base suffixes are case-sensitive. + /// + /// See [`case_sensitive_base_suffix`][Self::case_sensitive_base_suffix]. + pub const CASE_SENSITIVE_BASE_SUFFIX: bool = false; + + /// Get if base suffixes are case-sensitive. + /// + /// If set to [`true`], then the base suffix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn case_sensitive_base_suffix(&self) -> bool { + Self::CASE_SENSITIVE_BASE_SUFFIX + } + + // DIGIT SEPARATOR FLAGS & MASKS + + // If digit separators are allowed between integer digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. + /// + /// See [`integer_internal_digit_separator`][Self::integer_internal_digit_separator]. + pub const INTEGER_INTERNAL_DIGIT_SEPARATOR: bool = false; + + /// Get if digit separators are allowed between integer digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ✔️ | + /// | `1_` | ❌ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn integer_internal_digit_separator(&self) -> bool { + Self::INTEGER_INTERNAL_DIGIT_SEPARATOR + } + + /// If digit separators are allowed between fraction digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. + /// + /// See [`fraction_internal_digit_separator`][Self::fraction_internal_digit_separator]. + pub const FRACTION_INTERNAL_DIGIT_SEPARATOR: bool = false; + + /// Get if digit separators are allowed between fraction digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ✔️ | + /// | `1.1_` | ❌ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn fraction_internal_digit_separator(&self) -> bool { + Self::FRACTION_INTERNAL_DIGIT_SEPARATOR + } + + /// If digit separators are allowed between exponent digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. + /// + /// See [`exponent_internal_digit_separator`][Self::exponent_internal_digit_separator]. + pub const EXPONENT_INTERNAL_DIGIT_SEPARATOR: bool = false; + + /// Get if digit separators are allowed between exponent digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ✔️ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn exponent_internal_digit_separator(&self) -> bool { + Self::EXPONENT_INTERNAL_DIGIT_SEPARATOR + } + + /// If digit separators are allowed between digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. + /// + /// See [`internal_digit_separator`][Self::internal_digit_separator]. + pub const INTERNAL_DIGIT_SEPARATOR: bool = false; + + /// Get if digit separators are allowed between digits. + /// + /// This will not consider an input of only the digit separator + /// to be a valid separator: the digit separator must be surrounded by + /// digits. This is equivalent to any of + /// [`integer_internal_digit_separator`], + /// [`fraction_internal_digit_separator`], or + /// [`exponent_internal_digit_separator`] being set. + /// + /// [`integer_internal_digit_separator`]: Self::integer_internal_digit_separator + /// [`fraction_internal_digit_separator`]: Self::fraction_internal_digit_separator + /// [`exponent_internal_digit_separator`]: Self::exponent_internal_digit_separator + #[inline(always)] + pub const fn internal_digit_separator(&self) -> bool { + Self::INTERNAL_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed before any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`integer_leading_digit_separator`][Self::integer_leading_digit_separator]. + pub const INTEGER_LEADING_DIGIT_SEPARATOR: bool = false; + + /// Get if a digit separator is allowed before any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ❌ | + /// | `_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn integer_leading_digit_separator(&self) -> bool { + Self::INTEGER_LEADING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed before any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`fraction_leading_digit_separator`][Self::fraction_leading_digit_separator]. + pub const FRACTION_LEADING_DIGIT_SEPARATOR: bool = false; + + /// Get if a digit separator is allowed before any fraction digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ❌ | + /// | `1._1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn fraction_leading_digit_separator(&self) -> bool { + Self::FRACTION_LEADING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed before any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`exponent_leading_digit_separator`][Self::exponent_leading_digit_separator]. + pub const EXPONENT_LEADING_DIGIT_SEPARATOR: bool = false; + + /// Get if a digit separator is allowed before any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn exponent_leading_digit_separator(&self) -> bool { + Self::EXPONENT_LEADING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed before any digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`leading_digit_separator`][Self::leading_digit_separator]. + pub const LEADING_DIGIT_SEPARATOR: bool = false; + + /// Get if a digit separator is allowed before any digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. This is equivalent to + /// any of [`integer_leading_digit_separator`], + /// [`fraction_leading_digit_separator`], or + /// [`exponent_leading_digit_separator`] being set. + /// + /// [`integer_leading_digit_separator`]: Self::integer_leading_digit_separator + /// [`fraction_leading_digit_separator`]: Self::fraction_leading_digit_separator + /// [`exponent_leading_digit_separator`]: Self::exponent_leading_digit_separator + #[inline(always)] + pub const fn leading_digit_separator(&self) -> bool { + Self::LEADING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed after any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`integer_trailing_digit_separator`][Self::integer_trailing_digit_separator]. + pub const INTEGER_TRAILING_DIGIT_SEPARATOR: bool = false; + + /// Get if a digit separator is allowed after any integer digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ✔️ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn integer_trailing_digit_separator(&self) -> bool { + Self::INTEGER_TRAILING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed after any fraction digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`fraction_trailing_digit_separator`][Self::fraction_trailing_digit_separator]. + pub const FRACTION_TRAILING_DIGIT_SEPARATOR: bool = false; + + /// Get if a digit separator is allowed after any fraction digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ✔️ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn fraction_trailing_digit_separator(&self) -> bool { + Self::FRACTION_TRAILING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed after any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`exponent_trailing_digit_separator`][Self::exponent_trailing_digit_separator]. + pub const EXPONENT_TRAILING_DIGIT_SEPARATOR: bool = false; + + /// Get if a digit separator is allowed after any exponent digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ✔️ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn exponent_trailing_digit_separator(&self) -> bool { + Self::EXPONENT_TRAILING_DIGIT_SEPARATOR + } + + /// If a digit separator is allowed after any digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. + /// + /// See [`trailing_digit_separator`][Self::trailing_digit_separator]. + pub const TRAILING_DIGIT_SEPARATOR: bool = false; + + /// Get if a digit separator is allowed after any digits. + /// + /// This will consider an input of only the digit separator + /// to be a identical to empty input. This is equivalent to + /// any of [`integer_trailing_digit_separator`], + /// [`fraction_trailing_digit_separator`], or + /// [`exponent_trailing_digit_separator`] being set. + /// + /// [`integer_trailing_digit_separator`]: Self::integer_trailing_digit_separator + /// [`fraction_trailing_digit_separator`]: Self::fraction_trailing_digit_separator + /// [`exponent_trailing_digit_separator`]: Self::exponent_trailing_digit_separator + #[inline(always)] + pub const fn trailing_digit_separator(&self) -> bool { + Self::TRAILING_DIGIT_SEPARATOR + } + + /// If multiple consecutive integer digit separators are allowed. + /// + /// See [`integer_consecutive_digit_separator`][Self::integer_consecutive_digit_separator]. + pub const INTEGER_CONSECUTIVE_DIGIT_SEPARATOR: bool = false; + + /// Get if multiple consecutive integer digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// integer. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn integer_consecutive_digit_separator(&self) -> bool { + Self::INTEGER_CONSECUTIVE_DIGIT_SEPARATOR + } + + /// If multiple consecutive fraction digit separators are allowed. + /// + /// See [`fraction_consecutive_digit_separator`][Self::fraction_consecutive_digit_separator]. + pub const FRACTION_CONSECUTIVE_DIGIT_SEPARATOR: bool = false; + + /// Get if multiple consecutive fraction digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// fraction. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn fraction_consecutive_digit_separator(&self) -> bool { + Self::FRACTION_CONSECUTIVE_DIGIT_SEPARATOR + } + + /// If multiple consecutive exponent digit separators are allowed. + /// + /// See [`exponent_consecutive_digit_separator`][Self::exponent_consecutive_digit_separator]. + pub const EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR: bool = false; + + /// Get if multiple consecutive exponent digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// exponent. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn exponent_consecutive_digit_separator(&self) -> bool { + Self::EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR + } + + /// If multiple consecutive digit separators are allowed. + /// + /// See [`consecutive_digit_separator`][Self::consecutive_digit_separator]. + pub const CONSECUTIVE_DIGIT_SEPARATOR: bool = false; + + /// Get if multiple consecutive digit separators are allowed. + /// + /// This is equivalent to any of [`integer_consecutive_digit_separator`], + /// [`fraction_consecutive_digit_separator`], or + /// [`exponent_consecutive_digit_separator`] being set. + /// + /// [`integer_consecutive_digit_separator`]: Self::integer_consecutive_digit_separator + /// [`fraction_consecutive_digit_separator`]: Self::fraction_consecutive_digit_separator + /// [`exponent_consecutive_digit_separator`]: Self::exponent_consecutive_digit_separator + #[inline(always)] + pub const fn consecutive_digit_separator(&self) -> bool { + Self::CONSECUTIVE_DIGIT_SEPARATOR + } + + /// If any digit separators are allowed in special (non-finite) values. + /// + /// See [`special_digit_separator`][Self::special_digit_separator]. + pub const SPECIAL_DIGIT_SEPARATOR: bool = false; + + /// Get if any digit separators are allowed in special (non-finite) values. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for any special floats: for example, `N__a_N_` is considered + /// the same as `NaN`. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + #[inline(always)] + pub const fn special_digit_separator(&self) -> bool { + Self::SPECIAL_DIGIT_SEPARATOR + } + + // CHARACTERS + + /// The digit separator character in the packed struct. + /// + /// See [`digit_separator`][Self::digit_separator]. + pub const DIGIT_SEPARATOR: u8 = 0; + + /// Get the digit separator for the number format. + /// + /// Digit separators are frequently used in number literals to group + /// digits: `1,000,000` is a lot more readable than `1000000`, but + /// the `,` characters should be ignored in the parsing of the number. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `0`, or no digit separators allowed. + /// + /// # Examples + /// + /// Using a digit separator of `_` (note that the validity + /// oh where a digit separator can appear depends on the other digit + /// separator flags). + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1_4` | ✔️ | + /// | `+_14` | ✔️ | + /// | `+14e3_5` | ✔️ | + /// | `1_d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn digit_separator(&self) -> u8 { + Self::DIGIT_SEPARATOR + } + + /// Get if the format has a digit separator. + #[inline(always)] + pub const fn has_digit_separator(&self) -> bool { + self.digit_separator() != 0 + } + + /// The base prefix character in the packed struct. + /// + /// See [`base_prefix`][Self::base_prefix]. + pub const BASE_PREFIX: u8 = 0; + + /// Get the optional character for the base prefix. + /// + /// This character will come after a leading zero, so for example + /// setting the base prefix to `x` means that a leading `0x` will + /// be ignore, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to `0`, or no base prefix allowed. + /// + /// # Examples + /// + /// Using a base prefix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `0x1` | ✔️ | + /// | `x1` | ❌ | + /// | `1` | ✔️ | + /// | `1x` | ❌ | + /// | `1x1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn base_prefix(&self) -> u8 { + Self::BASE_PREFIX + } + + /// Get if the format has a base prefix. + #[inline(always)] + pub const fn has_base_prefix(&self) -> bool { + false + } + + /// The base suffix character in the packed struct. + /// + /// See [`base_suffix`][Self::base_suffix]. + pub const BASE_SUFFIX: u8 = 0; + + /// Get the optional character for the base suffix. + /// + /// This character will at the end of the buffer, so for example + /// setting the base prefix to `x` means that a trailing `x` will + /// be ignored, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to `0`, or no base suffix allowed. + /// + /// # Examples + /// + /// Using a base suffix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1x` | ✔️ | + /// | `1d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn base_suffix(&self) -> u8 { + Self::BASE_SUFFIX + } + + /// Get if the format has a base suffix. + #[inline(always)] + pub const fn has_base_suffix(&self) -> bool { + false + } + + // RADIX + + /// The radix for the significant digits in the packed struct. + /// + /// See [`mantissa_radix`][Self::mantissa_radix]. + pub const MANTISSA_RADIX: u32 = flags::mantissa_radix(FORMAT); + + /// Get the radix for mantissa digits. + /// + /// This is only used for the significant digits, that is, the integral and + /// fractional components. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults + /// to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "10011010010" | 1234 | + /// | 3 | "1200201" | 1234 | + /// | 8 | "2322" | 1234 | + /// | 10 | "1234" | 1234 | + /// | 16 | "4d2" | 1234 | + /// | 31 | "18p" | 1234 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// - Write Integer + #[inline(always)] + pub const fn mantissa_radix(&self) -> u32 { + Self::MANTISSA_RADIX + } + + /// The radix for the significant digits in the packed struct. + /// + /// Alias for [`MANTISSA_RADIX`][Self::MANTISSA_RADIX]. + pub const RADIX: u32 = Self::MANTISSA_RADIX; + + /// Get the radix for the significant digits. + /// + /// This is an alias for [`mantissa_radix`][Self::mantissa_radix]. + #[inline(always)] + pub const fn radix(&self) -> u32 { + Self::RADIX + } + + /// Get the `radix^2` for the significant digits. + #[inline(always)] + pub const fn radix2(&self) -> u32 { + self.radix().wrapping_mul(self.radix()) + } + + /// Get the `radix^4` for the significant digits. + #[inline(always)] + pub const fn radix4(&self) -> u32 { + self.radix2().wrapping_mul(self.radix2()) + } + + /// Get the `radix^8` for the significant digits. + #[inline(always)] + pub const fn radix8(&self) -> u32 { + self.radix4().wrapping_mul(self.radix4()) + } + + /// The base for the exponent. + /// + /// See [`exponent_base`][Self::exponent_base]. + pub const EXPONENT_BASE: u32 = flags::exponent_base(FORMAT); + + /// Get the radix for the exponent. + /// + /// For example, in `1.234e3`, it means `1.234 * 10^3`, and the exponent + /// base here is 10. Some programming languages, like C, support hex floats + /// with an exponent base of 2, for example `0x1.8p3`, or `1.5 * 2^3`. + /// Defaults to `10`. Can only be modified with [`feature`][crate#features] + /// `power-of-two` or `radix`. Defaults to `10`. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn exponent_base(&self) -> u32 { + Self::EXPONENT_BASE + } + + /// The radix for the exponent digits. + /// + /// See [`exponent_radix`][Self::exponent_radix]. + pub const EXPONENT_RADIX: u32 = flags::exponent_radix(FORMAT); + + /// Get the radix for exponent digits. + /// + /// This is only used for the exponent digits. We assume the radix for the + /// significant digits ([`mantissa_radix`][Self::mantissa_radix]) is + /// 10 as is the exponent base. Defaults to `10`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "1.234^1100" | 1.234e9 | + /// | 3 | "1.234^110" | 1.234e9 | + /// | 8 | "1.234^14" | 1.234e9 | + /// | 10 | "1.234^12" | 1.234e9 | + /// | 16 | "1.234^c" | 1.234e9 | + /// | 31 | "1.234^c" | 1.234e9 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + #[inline(always)] + pub const fn exponent_radix(&self) -> u32 { + Self::EXPONENT_RADIX + } + + // FLAGS + + /// Get the flags from the number format. + /// + /// This contains all the non-character and non-radix values + /// in the packed struct. + #[inline(always)] + pub const fn flags(&self) -> u128 { + FORMAT & flags::FLAG_MASK + } + + /// Get the interface flags from the number format. + /// + /// This contains all the flags that dictate code flows, and + /// therefore excludes logic like case-sensitive characters. + #[inline(always)] + pub const fn interface_flags(&self) -> u128 { + FORMAT & flags::INTERFACE_FLAG_MASK + } + + /// Get the digit separator flags from the number format. + #[inline(always)] + pub const fn digit_separator_flags(&self) -> u128 { + FORMAT & flags::DIGIT_SEPARATOR_FLAG_MASK + } + + /// Get the exponent flags from the number format. + /// + /// This contains all the flags pertaining to exponent + /// formats, including digit separators. + #[inline(always)] + pub const fn exponent_flags(&self) -> u128 { + FORMAT & flags::EXPONENT_FLAG_MASK + } + + /// Get the integer digit separator flags from the number format. + #[inline(always)] + pub const fn integer_digit_separator_flags(&self) -> u128 { + FORMAT & flags::INTEGER_DIGIT_SEPARATOR_FLAG_MASK + } + + /// Get the fraction digit separator flags from the number format. + #[inline(always)] + pub const fn fraction_digit_separator_flags(&self) -> u128 { + FORMAT & flags::FRACTION_DIGIT_SEPARATOR_FLAG_MASK + } + + /// Get the exponent digit separator flags from the number format. + #[inline(always)] + pub const fn exponent_digit_separator_flags(&self) -> u128 { + FORMAT & flags::EXPONENT_DIGIT_SEPARATOR_FLAG_MASK + } + + // BUILDER + + /// Get the number format builder from the format. + #[inline] + pub const fn builder() -> NumberFormatBuilder { + NumberFormatBuilder::new() + } + + /// Get the number format builder from the format. + #[inline] + pub const fn rebuild() -> NumberFormatBuilder { + NumberFormatBuilder::rebuild(FORMAT) + } +} + +impl Default for NumberFormat { + fn default() -> Self { + Self::new() + } +} + +/// Get if the radix is valid. +#[inline(always)] +pub(crate) const fn radix_error_impl(format: u128) -> Error { + if !flags::is_valid_radix(flags::mantissa_radix(format)) { + Error::InvalidMantissaRadix + } else if !flags::is_valid_radix(flags::exponent_base(format)) { + Error::InvalidExponentBase + } else if !flags::is_valid_radix(flags::exponent_radix(format)) { + Error::InvalidExponentRadix + } else { + Error::Success + } +} + +/// Get the error type from the format. +#[inline(always)] +pub(crate) const fn format_error_impl(format: u128) -> Error { + let valid_flags = flags::REQUIRED_EXPONENT_DIGITS | flags::REQUIRED_MANTISSA_DIGITS; + if !flags::is_valid_radix(flags::mantissa_radix(format)) { + Error::InvalidMantissaRadix + } else if !flags::is_valid_radix(flags::exponent_base(format)) { + Error::InvalidExponentBase + } else if !flags::is_valid_radix(flags::exponent_radix(format)) { + Error::InvalidExponentRadix + } else if !flags::is_valid_digit_separator(format) { + Error::InvalidDigitSeparator + } else if !flags::is_valid_base_prefix(format) { + Error::InvalidBasePrefix + } else if !flags::is_valid_base_suffix(format) { + Error::InvalidBaseSuffix + } else if !flags::is_valid_punctuation(format) { + Error::InvalidPunctuation + } else if (format & flags::FLAG_MASK) != valid_flags { + Error::InvalidFlags + } else { + Error::Success + } +} diff --git a/rust/vendor/lexical-util/src/num.rs b/rust/vendor/lexical-util/src/num.rs new file mode 100644 index 0000000..cd32dc6 --- /dev/null +++ b/rust/vendor/lexical-util/src/num.rs @@ -0,0 +1,1505 @@ +//! Utilities for Rust numbers. +//! +//! These traits define useful properties, methods, associated +//! types, and trait bounds, and conversions for working with +//! numbers in generic code. + +use core::{fmt, mem, ops}; + +#[cfg(feature = "f16")] +use crate::bf16::bf16; +#[cfg(feature = "f16")] +use crate::f16::f16; +#[cfg(all(not(feature = "std"), any(feature = "parse-floats", feature = "write-floats")))] +use crate::libm; + +// AS PRIMITIVE +// ------------ + +/// Type that can be converted to [`primitive`] values with `as`. +/// +/// [`primitive`]: https://doc.rust-lang.org/rust-by-example/primitives.html +pub trait AsPrimitive: Copy + PartialEq + PartialOrd + Send + Sync + Sized { + /// Convert the value to a [`u8`], as if by `value as u8`. + fn as_u8(self) -> u8; + + /// Convert the value to a [`u16`], as if by `value as u16`. + fn as_u16(self) -> u16; + + /// Convert the value to a [`u32`], as if by `value as u32`. + fn as_u32(self) -> u32; + + /// Convert the value to a [`u64`], as if by `value as u64`. + fn as_u64(self) -> u64; + + /// Convert the value to a [`u128`], as if by `value as u128`. + fn as_u128(self) -> u128; + + /// Convert the value to a [`usize`], as if by `value as usize`. + fn as_usize(self) -> usize; + + /// Convert the value to an [`i8`], as if by `value as i8`. + fn as_i8(self) -> i8; + + /// Convert the value to an [`i16`], as if by `value as i16`. + fn as_i16(self) -> i16; + + /// Convert the value to an [`i32`], as if by `value as i32`. + fn as_i32(self) -> i32; + + /// Convert the value to an [`i64`], as if by `value as i64`. + fn as_i64(self) -> i64; + + /// Convert the value to an [`i128`], as if by `value as i128`. + fn as_i128(self) -> i128; + + /// Convert the value to an [`isize`], as if by `value as isize`. + fn as_isize(self) -> isize; + + /// Convert the value to an [`f32`], as if by `value as f32`. + fn as_f32(self) -> f32; + + /// Convert the value to an [`f64`], as if by `value as f64`. + fn as_f64(self) -> f64; + + /// Convert the value from a [`u32`], as if by `value as _`. + fn from_u32(value: u32) -> Self; + + /// Convert the value from a [`u64`], as if by `value as _`. + fn from_u64(value: u64) -> Self; + + /// Convert the value to an [`struct@f16`], identical to `value as f16` + /// if [`struct@f16`] was a primitive type. + #[cfg(feature = "f16")] + fn as_f16(self) -> f16; + + /// Convert the value to an [`struct@bf16`], identical to `value as bf16` + /// if [`struct@bf16`] was a primitive type. + #[cfg(feature = "f16")] + fn as_bf16(self) -> bf16; +} + +macro_rules! as_primitive { + ($($t:ty)*) => ($( + impl AsPrimitive for $t { + #[inline(always)] + fn as_u8(self) -> u8 { + self as u8 + } + + #[inline(always)] + fn as_u16(self) -> u16 { + self as u16 + } + + #[inline(always)] + fn as_u32(self) -> u32 { + self as u32 + } + + #[inline(always)] + fn as_u64(self) -> u64 { + self as u64 + } + + #[inline(always)] + fn as_u128(self) -> u128 { + self as u128 + } + + #[inline(always)] + fn as_usize(self) -> usize { + self as usize + } + + #[inline(always)] + fn as_i8(self) -> i8 { + self as i8 + } + + #[inline(always)] + fn as_i16(self) -> i16 { + self as i16 + } + + #[inline(always)] + fn as_i32(self) -> i32 { + self as i32 + } + + #[inline(always)] + fn as_i64(self) -> i64 { + self as i64 + } + + #[inline(always)] + fn as_i128(self) -> i128 { + self as i128 + } + + #[inline(always)] + fn as_isize(self) -> isize { + self as isize + } + + #[inline(always)] + fn as_f32(self) -> f32 { + self as f32 + } + + #[inline(always)] + fn as_f64(self) -> f64 { + self as f64 + } + + #[inline(always)] + fn from_u32(value: u32) -> Self { + value as Self + } + + #[inline(always)] + fn from_u64(value: u64) -> Self { + value as Self + } + + #[cfg(feature = "f16")] + #[inline(always)] + fn as_f16(self) -> f16 { + f16::from_f32(self as f32) + } + + #[cfg(feature = "f16")] + #[inline(always)] + fn as_bf16(self) -> bf16 { + bf16::from_f32(self as f32) + } + } + )*) +} + +as_primitive! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize f32 f64 } + +#[cfg(feature = "f16")] +macro_rules! half_as_primitive { + ($($t:ty)*) => ($( + impl AsPrimitive for $t { + #[inline(always)] + fn as_u8(self) -> u8 { + self.as_f32() as u8 + } + + #[inline(always)] + fn as_u16(self) -> u16 { + self.as_f32() as u16 + } + + #[inline(always)] + fn as_u32(self) -> u32 { + self.as_f32() as u32 + } + + #[inline(always)] + fn as_u64(self) -> u64 { + self.as_f32() as u64 + } + + #[inline(always)] + fn as_u128(self) -> u128 { + self.as_f32() as u128 + } + + #[inline(always)] + fn as_usize(self) -> usize { + self.as_f32() as usize + } + + #[inline(always)] + fn as_i8(self) -> i8 { + self.as_f32() as i8 + } + + #[inline(always)] + fn as_i16(self) -> i16 { + self.as_f32() as i16 + } + + #[inline(always)] + fn as_i32(self) -> i32 { + self.as_f32() as i32 + } + + #[inline(always)] + fn as_i64(self) -> i64 { + self.as_f32() as i64 + } + + #[inline(always)] + fn as_i128(self) -> i128 { + self.as_f32() as i128 + } + + #[inline(always)] + fn as_isize(self) -> isize { + self.as_f32() as isize + } + + #[inline(always)] + fn as_f32(self) -> f32 { + self.as_f32() as f32 + } + + #[inline(always)] + fn as_f64(self) -> f64 { + self.as_f32() as f64 + } + + #[inline(always)] + #[allow(clippy::as_underscore)] // reason="intentionally used in a generic sense" + fn from_u32(value: u32) -> Self { + Self::from_f32(value as _) + } + + #[inline(always)] + fn from_u64(value: u64) -> Self { + _ = value; + unimplemented!() + } + + #[inline(always)] + fn as_f16(self) -> f16 { + f16::from_f32(self.as_f32()) + } + + #[inline(always)] + fn as_bf16(self) -> bf16 { + bf16::from_f32(self.as_f32()) + } + } + )*) +} + +#[cfg(feature = "f16")] +half_as_primitive! { f16 bf16 } + +// AS CAST +// ------- + +/// An interface for casting between machine scalars, as if `as` was used. +/// +/// All values that the type can be cast to must be [`primitive`] values. +/// +/// [`primitive`]: https://doc.rust-lang.org/rust-by-example/primitives.html +pub trait AsCast: AsPrimitive { + /// Creates a number from another value that can be converted into + /// a primitive via the [`AsPrimitive`] trait. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::AsCast; + /// + /// assert_eq!(u8::as_cast(256u16), 256u16 as u8); // 0 + /// ``` + fn as_cast(n: N) -> Self; +} + +/// Allows the high-level conversion of generic types as if `as` was used. +/// +/// # Examples +/// +/// ```rust +/// use lexical_util::num::as_cast; +/// +/// assert_eq!(as_cast::(256u16), 256u16 as u8); // 0 +/// ``` +#[inline(always)] +pub fn as_cast(t: T) -> U { + U::as_cast(t) +} + +macro_rules! as_cast { + ($($t:ty, $meth:ident ; )*) => ($( + impl AsCast for $t { + #[inline(always)] + #[allow(clippy::as_underscore)] // reason="intentional due to generic API" + fn as_cast(n: N) -> $t { + n.$meth() as _ + } + } + )*); +} + +as_cast!( + u8, as_u8 ; + u16, as_u16 ; + u32, as_u32 ; + u64, as_u64 ; + u128, as_u128 ; + usize, as_usize ; + i8, as_i8 ; + i16, as_i16 ; + i32, as_i32 ; + i64, as_i64 ; + i128, as_i128 ; + isize, as_isize ; + f32, as_f32 ; + f64, as_f64 ; +); + +#[cfg(feature = "f16")] +as_cast!( + f16, as_f16 ; + bf16, as_bf16 ; +); + +// PRIMITIVE +// --------- + +/// The base trait for all [`primitive`] types. +/// +/// [`primitive`]: https://doc.rust-lang.org/rust-by-example/primitives.html +pub trait Primitive: 'static + fmt::Debug + fmt::Display + AsCast {} + +macro_rules! primitive { + ($($t:ty)*) => ($( + impl Primitive for $t {} + )*) +} + +primitive! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize f32 f64 } + +#[cfg(feature = "f16")] +primitive! { f16 bf16 } + +// NUMBER +// ------ + +/// The base trait for all numbers (integers and floating-point numbers). +pub trait Number: + Default + + Primitive + + // Operations + ops::Add + + ops::AddAssign + + ops::Div + + ops::DivAssign + + ops::Mul + + ops::MulAssign + + ops::Rem + + ops::RemAssign + + ops::Sub + + ops::SubAssign +{ + /// If the number can hold negative values. + const IS_SIGNED: bool; +} + +macro_rules! number_impl { + ($($t:tt $is_signed:literal ; )*) => ($( + impl Number for $t { + const IS_SIGNED: bool = $is_signed; + } + )*) +} + +number_impl! { + u8 false ; + u16 false ; + u32 false ; + u64 false ; + u128 false ; + usize false ; + i8 true ; + i16 true ; + i32 true ; + i64 true ; + i128 true ; + isize true ; + f32 true ; + f64 true ; + // f128 true +} + +#[cfg(feature = "f16")] +number_impl! { + f16 true ; + bf16 true ; +} + +// INTEGER +// ------- + +/// The base trait for all signed and unsigned [`integers`]. +/// +/// [`integers`]: https://en.wikipedia.org/wiki/Integer_(computer_science) +pub trait Integer: + // Basic + Number + Eq + Ord + + // Operations + ops::BitAnd + + ops::BitAndAssign + + ops::BitOr + + ops::BitOrAssign + + ops::BitXor + + ops::BitXorAssign + + ops::Not + + ops::Shl + + ops::Shl + + ops::ShlAssign + + ops::Shr + + ops::ShrAssign + +{ + // CONSTANTS + /// A value equal to `0`. + const ZERO: Self; + + /// A value equal to `1`. + const ONE: Self; + + /// A value equal to `2`. + const TWO: Self; + + /// The largest value that can be represented by this integer type. + /// + /// See [`u32::MAX`]. + const MAX: Self; + + /// The smallest value that can be represented by this integer type. + /// + /// See [`u32::MIN`]. + const MIN: Self; + + /// The size of this integer type in bits. + /// + /// See [`u32::BITS`]. + const BITS: usize; + + // FUNCTIONS (INHERITED) + /// Returns the number of leading zeros in the binary representation + /// of `self`. + /// + /// See [`u32::leading_zeros`]. + fn leading_zeros(self) -> u32; + + /// Returns the number of trailing zeros in the binary representation + /// of `self`. + /// + /// See [`u32::trailing_zeros`]. + fn trailing_zeros(self) -> u32; + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// See [`u32::pow`]. + fn pow(self, exp: u32) -> Self; + + /// Checked exponentiation. Computes `self.pow(exp)`, returning + /// `None` if overflow occurred. + /// + /// See [`u32::checked_pow`]. + fn checked_pow(self, exp: u32) -> Option; + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// Returns a tuple of the exponentiation along with a bool indicating + /// whether an overflow happened. + /// + /// See [`u32::overflowing_pow`]. + fn overflowing_pow(self, exp: u32) -> (Self, bool); + + /// Checked integer addition. Computes `self + i`, returning `None` if + /// overflow occurred. + /// + /// See [`u32::checked_add`]. + fn checked_add(self, i: Self) -> Option; + + /// Checked integer subtraction. Computes `self - i`, returning `None` + /// if overflow occurred. + /// + /// See [`u32::checked_sub`]. + fn checked_sub(self, i: Self) -> Option; + + /// Checked integer multiplication. Computes `self * rhs`, returning `None` + /// if overflow occurred. + /// + /// See [`u32::checked_mul`]. + fn checked_mul(self, i: Self) -> Option; + + /// Calculates `self + i`. + /// + /// Returns a tuple of the addition along with a boolean indicating whether + /// an arithmetic overflow would occur. If an overflow would have occurred + /// then the wrapped value is returned. See [`u32::overflowing_add`]. + fn overflowing_add(self, i: Self) -> (Self, bool); + + /// Calculates `self - i`. + /// + /// Returns a tuple of the addition along with a boolean indicating whether + /// an arithmetic overflow would occur. If an overflow would have occurred + /// then the wrapped value is returned. See [`u32::overflowing_sub`]. + fn overflowing_sub(self, i: Self) -> (Self, bool); + + /// Calculates `self * i`. + /// + /// Returns a tuple of the addition along with a boolean indicating whether + /// an arithmetic overflow would occur. If an overflow would have occurred + /// then the wrapped value is returned. See [`u32::overflowing_mul`]. + fn overflowing_mul(self, i: Self) -> (Self, bool); + + /// Wrapping (modular) addition. Computes `self + i`, wrapping around at + /// the boundary of the type. + /// + /// See [`u32::wrapping_add`]. + fn wrapping_add(self, i: Self) -> Self; + + /// Wrapping (modular) subtraction. Computes `self - i`, wrapping around at + /// the boundary of the type. + /// + /// See [`u32::wrapping_sub`]. + fn wrapping_sub(self, i: Self) -> Self; + + /// Wrapping (modular) multiplication. Computes `self * i`, wrapping around at + /// the boundary of the type. + /// + /// See [`u32::wrapping_mul`]. + fn wrapping_mul(self, i: Self) -> Self; + + /// Wrapping (modular) negation. Computes `-self`, wrapping around at + /// the boundary of the type. + /// + /// See [`u32::wrapping_neg`]. + fn wrapping_neg(self) -> Self; + + /// Saturating integer addition. Computes `self + i`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// See [`u32::saturating_add`]. + fn saturating_add(self, i: Self) -> Self; + + /// Saturating integer subtraction. Computes `self - i`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// See [`u32::saturating_sub`]. + fn saturating_sub(self, i: Self) -> Self; + + /// Saturating integer multiplication. Computes `self * i`, saturating at + /// the numeric bounds instead of overflowing. + /// + /// See [`u32::saturating_mul`]. + fn saturating_mul(self, i: Self) -> Self; + + /// Get the fast ceiling of the quotient from integer division. + /// + /// The remainder may wrap to the numerical boundaries for the type. + /// See [`u32::div_ceil`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Integer; + /// + /// assert_eq!(250u16.ceil_divmod(10), (25, 0)); + /// assert_eq!(256u16.ceil_divmod(10), (26, -4)); + /// assert_eq!(i32::MAX.ceil_divmod(-2), (-0x3FFFFFFE, 3)); + /// + /// // notice how `-1` wraps since `i32` cannot hold `i128::MAX`. + /// assert_eq!((i128::MAX - 1).ceil_divmod(i128::MAX), (1, -1)); + /// ``` + #[inline(always)] + fn ceil_divmod(self, y: Self) -> (Self, i32) { + let q = self / y; + let r = self % y; + match r == Self::ZERO { + true => (q, i32::as_cast(r)), + false => (q + Self::ONE, i32::as_cast(r) - i32::as_cast(y)) + } + } + + /// Get the fast ceiling of the quotient from integer division. + /// + /// This is identical to [`u32::div_ceil`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Integer; + /// + /// assert_eq!(250u16.ceil_div(10), 25); + /// assert_eq!(256u16.ceil_div(10), 26); + /// assert_eq!(i32::MAX.ceil_div(-2), -0x3FFFFFFE); + /// assert_eq!((i128::MAX - 1).ceil_div(i128::MAX), 1); + /// ``` + #[inline(always)] + fn ceil_div(self, y: Self) -> Self { + self.ceil_divmod(y).0 + } + + /// Get the fast ceiling modulus from integer division. + /// + /// The remainder is not guaranteed to be valid since it can + /// overflow if the remainder is not 0. See [`Self::ceil_divmod`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Integer; + /// + /// assert_eq!(250u16.ceil_mod(10), 0); + /// assert_eq!(256u16.ceil_mod(10), -4); + /// assert_eq!(i32::MAX.ceil_mod(-2), 3); + /// + /// // notice how `-1` wraps since `i32` cannot hold `i128::MAX`. + /// assert_eq!((i128::MAX - 1).ceil_mod(i128::MAX), -1); + /// ``` + #[inline(always)] + fn ceil_mod(self, y: Self) -> i32 { + self.ceil_divmod(y).1 + } + + // PROPERTIES + + /// Get the number of bits in a value. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Integer; + /// + /// assert_eq!(1u64.bit_length(), 1); + /// assert_eq!(2u64.bit_length(), 2); + /// assert_eq!(3u64.bit_length(), 2); + /// assert_eq!(16u64.bit_length(), 5); + /// ``` + #[inline(always)] + fn bit_length(self) -> u32 { + Self::BITS as u32 - self.leading_zeros() + } + + /// Returns true if the least-significant bit is odd. + #[inline(always)] + fn is_odd(self) -> bool { + self & Self::ONE == Self::ONE + } + + /// Returns true if the least-significant bit is even. + #[inline(always)] + fn is_even(self) -> bool { + !self.is_odd() + } + + /// Get the maximum number of digits before the slice will overflow. + /// + /// This is effectively the `floor(log(2^BITS-1, radix))`, but we can + /// try to go a bit lower without worrying too much. + #[inline(always)] + fn overflow_digits(radix: u32) -> usize { + // this is heavily optimized for base10 and it's a way under estimate + // that said, it's fast and works. + if radix <= 16 { + mem::size_of::() * 2 - Self::IS_SIGNED as usize + } else { + // way under approximation but always works and is fast + mem::size_of::() + } + } +} + +macro_rules! integer_impl { +($($t:tt)*) => ($( + impl Integer for $t { + const ZERO: $t = 0; + const ONE: $t = 1; + const TWO: $t = 2; + const MAX: $t = $t::MAX; + const MIN: $t = $t::MIN; + const BITS: usize = $t::BITS as usize; + + #[inline(always)] + fn leading_zeros(self) -> u32 { + $t::leading_zeros(self) + } + + #[inline(always)] + fn trailing_zeros(self) -> u32 { + $t::trailing_zeros(self) + } + + #[inline(always)] + fn checked_add(self, i: Self) -> Option { + $t::checked_add(self, i) + } + + #[inline(always)] + fn checked_sub(self, i: Self) -> Option { + $t::checked_sub(self, i) + } + + #[inline(always)] + fn checked_mul(self, i: Self) -> Option { + $t::checked_mul(self, i) + } + + #[inline(always)] + fn overflowing_add(self, i: Self) -> (Self, bool) { + $t::overflowing_add(self, i) + } + + #[inline(always)] + fn overflowing_sub(self, i: Self) -> (Self, bool) { + $t::overflowing_sub(self, i) + } + + #[inline(always)] + fn overflowing_mul(self, i: Self) -> (Self, bool) { + $t::overflowing_mul(self, i) + } + + #[inline(always)] + fn wrapping_add(self, i: Self) -> Self { + $t::wrapping_add(self, i) + } + + #[inline(always)] + fn wrapping_sub(self, i: Self) -> Self { + $t::wrapping_sub(self, i) + } + + #[inline(always)] + fn wrapping_mul(self, i: Self) -> Self { + $t::wrapping_mul(self, i) + } + + #[inline(always)] + fn wrapping_neg(self) -> Self { + $t::wrapping_neg(self) + } + + #[inline(always)] + fn pow(self, exp: u32) -> Self { + Self::pow(self, exp) + } + + #[inline(always)] + fn checked_pow(self, exp: u32) -> Option { + Self::checked_pow(self, exp) + } + + #[inline(always)] + fn overflowing_pow(self, exp: u32) -> (Self, bool) { + Self::overflowing_pow(self, exp) + } + + #[inline(always)] + fn saturating_add(self, i: Self) -> Self { + $t::saturating_add(self, i) + } + + #[inline(always)] + fn saturating_sub(self, i: Self) -> Self { + $t::saturating_sub(self, i) + } + + #[inline(always)] + fn saturating_mul(self, i: Self) -> Self { + $t::saturating_mul(self, i) + } + } +)*) +} + +integer_impl! { u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 usize isize } + +// SIGNED INTEGER +// -------------- + +/// The trait for types that support [`signed`] integral operations, that is, +/// they can hold negative numbers. +/// +/// [`signed`]: https://en.wikipedia.org/wiki/Integer_(computer_science)#Value_and_representation +pub trait SignedInteger: Integer + ops::Neg {} + +macro_rules! signed_integer_impl { +($($t:tt)*) => ($( + impl SignedInteger for $t {} +)*) +} + +signed_integer_impl! { i8 i16 i32 i64 i128 isize } + +// UNSIGNED INTEGER +// ---------------- + +/// The trait for types that support [`unsigned`] integral operations, that is, +/// they can only hold positive numbers. +/// +/// [`unsigned`]: https://en.wikipedia.org/wiki/Integer_(computer_science)#Value_and_representation +pub trait UnsignedInteger: Integer {} + +macro_rules! unsigned_integer_impl { +($($t:ty)*) => ($( + impl UnsignedInteger for $t {} +)*) +} + +unsigned_integer_impl! { u8 u16 u32 u64 u128 usize } + +// FLOAT +// ----- + +/// The trait for floating-point [`numbers`][`floats`]. +/// +/// Floating-point numbers are numbers that may contain a fraction +/// and are stored internally as the significant digits and an +/// exponent of base 2. +/// +/// [`floats`]: https://en.wikipedia.org/wiki/Floating-point_arithmetic +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] +pub trait Float: Number + ops::Neg { + /// Unsigned type of the same size. + type Unsigned: UnsignedInteger; + + // CONSTANTS + + /// A value equal to `0`. + const ZERO: Self; + + /// A value equal to `1`. + const ONE: Self; + + /// A value equal to `2`. + const TWO: Self; + + /// Largest finite value. + /// + /// See [`f64::MAX`]. + const MAX: Self; + + /// Smallest finite value. + /// + /// See [`f64::MIN`]. + const MIN: Self; + + /// Infinity (`∞`). + /// + /// See [`f64::INFINITY`]. + const INFINITY: Self; + + /// Negative infinity (`−∞`). + /// + /// See [`f64::NEG_INFINITY`]. + const NEG_INFINITY: Self; + + /// Not a Number (NaN). + /// + /// See [`f64::NAN`]. + const NAN: Self; + + /// The size of this float type in bits. + /// + /// Analogous to [`u32::BITS`]. + const BITS: usize; + + /// Bitmask to extract the sign from the float. + const SIGN_MASK: Self::Unsigned; + + /// Bitmask to extract the biased exponent, including the hidden bit. + const EXPONENT_MASK: Self::Unsigned; + + /// Bitmask to extract the hidden bit in the exponent, which is an + /// implicit 1 in the significant digits. + const HIDDEN_BIT_MASK: Self::Unsigned; + + /// Bitmask to extract the mantissa (significant digits), excluding + /// the hidden bit. + const MANTISSA_MASK: Self::Unsigned; + + /// Mask to determine if a full-carry occurred (1 in bit above hidden bit). + const CARRY_MASK: Self::Unsigned; + + // PROPERTIES + + // The following constants can be calculated as follows: + // - `INFINITY_BITS`: EXPONENT_MASK + // - `NEGATIVE_INFINITY_BITS`: INFINITY_BITS | SIGN_MASK + // - `EXPONENT_BIAS`: `2^(EXPONENT_SIZE-1) - 1 + MANTISSA_SIZE` + // - `DENORMAL_EXPONENT`: `1 - EXPONENT_BIAS` + // - `MAX_EXPONENT`: `2^EXPONENT_SIZE - 1 - EXPONENT_BIAS` + + /// Positive infinity as bits. + const INFINITY_BITS: Self::Unsigned; + + /// Positive infinity as bits. + const NEGATIVE_INFINITY_BITS: Self::Unsigned; + + /// The number of bits in the exponent. + const EXPONENT_SIZE: i32; + + /// Size of the significand (mantissa) without the hidden bit. + const MANTISSA_SIZE: i32; + + /// Bias of the exponent. See [`exponent bias`]. + /// + /// [`exponent bias`]: https://en.wikipedia.org/wiki/Exponent_bias + const EXPONENT_BIAS: i32; + + /// Exponent portion of a [`denormal`] float. + /// + /// [`denormal`]: https://en.wikipedia.org/wiki/Subnormal_number + const DENORMAL_EXPONENT: i32; + + /// Maximum (unbiased) exponent value in the float. + const MAX_EXPONENT: i32; + + // FUNCTIONS (INHERITED) + + // Re-export the to and from bits methods. + + /// Raw transmutation to the unsigned integral type. + /// + /// See [`f64::to_bits`]. + fn to_bits(self) -> Self::Unsigned; + + /// Raw transmutation from the unsigned integral type. + /// + /// See [`f64::from_bits`]. + fn from_bits(u: Self::Unsigned) -> Self; + + /// Returns the natural logarithm of the number. + /// + /// See [`f64::ln`]. + fn ln(self) -> Self; + + /// Returns the largest integer less than or equal to `self`. + /// + /// See [`f64::floor`]. + fn floor(self) -> Self; + + /// Returns true if `self` has a positive sign, including `+0.0`, + /// NaNs with positive sign bit and positive infinity. + /// + /// See [`f64::is_sign_positive`]. + fn is_sign_positive(self) -> bool; + + /// Returns true if `self` has a negative sign, including `-0.0`, + /// NaNs with negative sign bit and negative infinity. + /// + /// See [`f64::is_sign_negative`]. + fn is_sign_negative(self) -> bool; + + /// Returns true if the float is [`denormal`]. + /// + /// Denormal (subnormal) numbers fall below the range of numbers + /// that can be stored as `mantissa * 2^exp`, and therefore + /// always have the minimum exponent. + /// + /// [`denormal`]: https://en.wikipedia.org/wiki/Subnormal_number + #[inline(always)] + fn is_denormal(self) -> bool { + self.to_bits() & Self::EXPONENT_MASK == Self::Unsigned::ZERO + } + + /// Returns true if the float is NaN, positive infinity, or negative + /// infinity. + #[inline(always)] + fn is_special(self) -> bool { + self.to_bits() & Self::EXPONENT_MASK == Self::EXPONENT_MASK + } + + /// Returns true if the float is NaN. + #[inline(always)] + fn is_nan(self) -> bool { + self.is_special() && (self.to_bits() & Self::MANTISSA_MASK) != Self::Unsigned::ZERO + } + + /// Returns true if the float is positive or negative infinity. + #[inline(always)] + fn is_inf(self) -> bool { + self.is_special() && (self.to_bits() & Self::MANTISSA_MASK) == Self::Unsigned::ZERO + } + + /// Returns true if the float's least-significant mantissa bit is odd. + #[inline(always)] + fn is_odd(self) -> bool { + self.to_bits().is_odd() + } + + /// Returns true if the float's least-significant mantissa bit is even. + #[inline(always)] + fn is_even(self) -> bool { + !self.is_odd() + } + + /// Returns true if the float needs a negative sign when serializing it. + /// + /// This is true if it's `-0.0` or it's below 0 and not NaN. But inf values + /// need the sign. + #[inline(always)] + fn needs_negative_sign(self) -> bool { + self.is_sign_negative() && !self.is_nan() + } + + /// Get the unbiased exponent component from the float. + #[inline(always)] + fn exponent(self) -> i32 { + if self.is_denormal() { + return Self::DENORMAL_EXPONENT; + } + + let bits = self.to_bits(); + let biased_e = i32::as_cast((bits & Self::EXPONENT_MASK) >> Self::MANTISSA_SIZE).as_i32(); + biased_e - Self::EXPONENT_BIAS + } + + /// Get the mantissa (significand) component from float. + #[inline(always)] + fn mantissa(self) -> Self::Unsigned { + let bits = self.to_bits(); + let s = bits & Self::MANTISSA_MASK; + if !self.is_denormal() { + s + Self::HIDDEN_BIT_MASK + } else { + s + } + } + + /// Get next greater float. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Float; + /// + /// assert_eq!(1f32.next(), 1.0000001); + /// assert_eq!((-0.0f32).next(), 0.0); // +0.0 + /// assert_eq!(0f32.next(), 1e-45); + /// ``` + #[inline(always)] + fn next(self) -> Self { + let bits = self.to_bits(); + if self.is_sign_negative() && self == Self::ZERO { + // -0.0 + Self::ZERO + } else if bits == Self::INFINITY_BITS { + Self::from_bits(Self::INFINITY_BITS) + } else if self.is_sign_negative() { + Self::from_bits(bits.saturating_sub(Self::Unsigned::ONE)) + } else { + Self::from_bits(bits.saturating_add(Self::Unsigned::ONE)) + } + } + + /// Get next greater float for a positive float. + /// + /// Value must be >= 0.0 and < INFINITY. + #[inline(always)] + fn next_positive(self) -> Self { + debug_assert!(self.is_sign_positive() && !self.is_inf()); + Self::from_bits(self.to_bits() + Self::Unsigned::ONE) + } + + /// Get previous greater float, such that `self.prev().next() == self`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Float; + /// + /// assert_eq!(1f32.prev(), 0.99999994); + /// assert_eq!(0.0f32.prev(), 0.0); // -0.0 + /// assert_eq!((-0.0f32).prev(), -1e-45); + /// ``` + #[inline(always)] + fn prev(self) -> Self { + let bits = self.to_bits(); + if self.is_sign_positive() && self == Self::ZERO { + // +0.0 + -Self::ZERO + } else if bits == Self::NEGATIVE_INFINITY_BITS { + Self::from_bits(Self::NEGATIVE_INFINITY_BITS) + } else if self.is_sign_negative() { + Self::from_bits(bits.saturating_add(Self::Unsigned::ONE)) + } else { + Self::from_bits(bits.saturating_sub(Self::Unsigned::ONE)) + } + } + + /// Get previous greater float for a positive float. + /// Value must be > 0.0. + #[inline(always)] + fn prev_positive(self) -> Self { + debug_assert!(self.is_sign_positive() && self != Self::ZERO); + Self::from_bits(self.to_bits() - Self::Unsigned::ONE) + } + + /// Round a positive number to even. + #[inline(always)] + fn round_positive_even(self) -> Self { + if self.mantissa().is_odd() { + self.next_positive() + } else { + self + } + } + + /// Get the max of two finite numbers. + /// + /// This assumes that both floats form a [`total ord`], + /// that is, `x < y` is always `y >= x`. Non-finite floats, + /// such as NaN, break this criteria, but finite floats enable + /// simpler (and faster) comparison criteria while remaining + /// accurate. + /// + /// [`total ord`]: https://doc.rust-lang.org/std/cmp/trait.Ord.html + #[inline(always)] + fn max_finite(self, f: Self) -> Self { + debug_assert!(!self.is_special() && !f.is_special(), "max_finite self={} f={}", self, f); + if self < f { + f + } else { + self + } + } + + /// Get the min of two finite numbers. + /// + /// This assumes that both floats form a [`total ord`], + /// that is, `x < y` is always `y >= x`. Non-finite floats, + /// such as NaN, break this criteria, but finite floats enable + /// simpler (and faster) comparison criteria while remaining + /// accurate. + /// + /// [`total ord`]: https://doc.rust-lang.org/std/cmp/trait.Ord.html + #[inline(always)] + fn min_finite(self, f: Self) -> Self { + debug_assert!(!self.is_special() && !f.is_special(), "min_finite self={} f={}", self, f); + if self < f { + self + } else { + f + } + } +} + +/// Define the float literals. +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] +macro_rules! float_literals { + ($float:ty) => { + const ZERO: $float = 0.0; + const ONE: $float = 1.0; + const TWO: $float = 2.0; + const MAX: $float = <$float>::MAX; + const MIN: $float = <$float>::MIN; + const INFINITY: $float = <$float>::INFINITY; + const NEG_INFINITY: $float = <$float>::NEG_INFINITY; + const NAN: $float = <$float>::NAN; + const BITS: usize = mem::size_of::<$float>() * 8; + }; +} + +/// Define the float masks. +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] +macro_rules! float_masks { + ( + float => + $float:ty,sign_mask => + $sign:literal,exponent_mask => + $exponent:literal,hidden_bit_mask => + $hidden:literal,mantissa_mask => + $mantissa:literal, + ) => { + const SIGN_MASK: <$float>::Unsigned = $sign; + const EXPONENT_MASK: <$float>::Unsigned = $exponent; + const HIDDEN_BIT_MASK: <$float>::Unsigned = $hidden; + const MANTISSA_MASK: <$float>::Unsigned = $mantissa; + // The carry mask is always 1 bit above the hidden bit. + const CARRY_MASK: <$float>::Unsigned = $hidden << 1; + // Infinity is always every exponent bit set. + const INFINITY_BITS: <$float>::Unsigned = $exponent; + // Negative infinity is just infinity + sign. + const NEGATIVE_INFINITY_BITS: <$float>::Unsigned = $exponent | $sign; + }; +} + +// Due to missing specifics or types for the following float types, +// `Float` is not yet fully implemented for: +// - f128 + +#[cfg(feature = "f16")] +macro_rules! float_one { + ($f:ident) => { + (($f::EXPONENT_BIAS - $f::MANTISSA_SIZE) as u16) << $f::MANTISSA_SIZE + }; +} + +#[cfg(feature = "f16")] +macro_rules! float_two { + ($f:ident) => { + (($f::EXPONENT_BIAS - $f::MANTISSA_SIZE + 1) as u16) << $f::MANTISSA_SIZE + }; +} + +#[cfg(feature = "f16")] +macro_rules! float_max { + ($f:ident) => { + ($f::EXPONENT_MASK ^ $f::HIDDEN_BIT_MASK) | $f::MANTISSA_MASK + }; +} + +#[cfg(feature = "f16")] +macro_rules! float_min { + ($f:ident) => { + $f::MAX.to_bits() | $f::SIGN_MASK + }; +} + +#[cfg(feature = "f16")] +macro_rules! float_nan { + ($f:ident) => { + $f::EXPONENT_MASK | ($f::HIDDEN_BIT_MASK >> 1) + }; +} + +#[cfg(feature = "f16")] +impl Float for f16 { + type Unsigned = u16; + + const ZERO: Self = Self::from_bits(0); + const ONE: Self = Self::from_bits(float_one!(Self)); + const TWO: Self = Self::from_bits(float_two!(Self)); + const MAX: Self = Self::from_bits(float_max!(Self)); + const MIN: Self = Self::from_bits(float_min!(Self)); + const INFINITY: Self = Self::from_bits(Self::INFINITY_BITS); + const NEG_INFINITY: Self = Self::from_bits(Self::NEGATIVE_INFINITY_BITS); + const NAN: Self = Self::from_bits(float_nan!(Self)); + const BITS: usize = mem::size_of::() * 8; + + float_masks!( + float => Self, + sign_mask => 0x8000, + exponent_mask => 0x7C00, + hidden_bit_mask => 0x0400, + mantissa_mask => 0x03FF, + ); + const EXPONENT_SIZE: i32 = 5; + const MANTISSA_SIZE: i32 = 10; + const EXPONENT_BIAS: i32 = 15 + Self::MANTISSA_SIZE; + const DENORMAL_EXPONENT: i32 = 1 - Self::EXPONENT_BIAS; + const MAX_EXPONENT: i32 = 0x1F - Self::EXPONENT_BIAS; + + #[inline(always)] + fn to_bits(self) -> u16 { + f16::to_bits(self) + } + + #[inline(always)] + fn from_bits(u: u16) -> f16 { + f16::from_bits(u) + } + + #[inline(always)] + fn ln(self) -> f16 { + f16::from_f32(self.as_f32().ln()) + } + + #[inline(always)] + fn floor(self) -> f16 { + f16::from_f32(self.as_f32().floor()) + } + + #[inline(always)] + fn is_sign_positive(self) -> bool { + self.to_bits() & Self::SIGN_MASK == 0 + } + + #[inline(always)] + fn is_sign_negative(self) -> bool { + !self.is_sign_positive() + } +} + +#[cfg(feature = "f16")] +impl Float for bf16 { + type Unsigned = u16; + + const ZERO: Self = Self::from_bits(0); + const ONE: Self = Self::from_bits(float_one!(Self)); + const TWO: Self = Self::from_bits(float_two!(Self)); + const MAX: Self = Self::from_bits(float_max!(Self)); + const MIN: Self = Self::from_bits(float_min!(Self)); + const INFINITY: Self = Self::from_bits(Self::INFINITY_BITS); + const NEG_INFINITY: Self = Self::from_bits(Self::NEGATIVE_INFINITY_BITS); + const NAN: Self = Self::from_bits(float_nan!(Self)); + const BITS: usize = mem::size_of::() * 8; + + float_masks!( + float => Self, + sign_mask => 0x8000, + exponent_mask => 0x7F80, + hidden_bit_mask => 0x0080, + mantissa_mask => 0x007F, + ); + const EXPONENT_SIZE: i32 = 8; + const MANTISSA_SIZE: i32 = 7; + const EXPONENT_BIAS: i32 = 127 + Self::MANTISSA_SIZE; + const DENORMAL_EXPONENT: i32 = 1 - Self::EXPONENT_BIAS; + const MAX_EXPONENT: i32 = 0xFF - Self::EXPONENT_BIAS; + + #[inline(always)] + fn to_bits(self) -> u16 { + bf16::to_bits(self) + } + + #[inline(always)] + fn from_bits(u: u16) -> bf16 { + bf16::from_bits(u) + } + + #[inline(always)] + fn ln(self) -> bf16 { + bf16::from_f32(self.as_f32().ln()) + } + + #[inline(always)] + fn floor(self) -> bf16 { + bf16::from_f32(self.as_f32().floor()) + } + + #[inline(always)] + fn is_sign_positive(self) -> bool { + self.to_bits() & Self::SIGN_MASK == 0 + } + + #[inline(always)] + fn is_sign_negative(self) -> bool { + !self.is_sign_positive() + } +} + +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] +impl Float for f32 { + type Unsigned = u32; + float_literals!(f32); + float_masks!( + float => Self, + sign_mask => 0x80000000, + exponent_mask => 0x7F800000, + hidden_bit_mask => 0x00800000, + mantissa_mask => 0x007FFFFF, + ); + const EXPONENT_SIZE: i32 = 8; + const MANTISSA_SIZE: i32 = 23; + const EXPONENT_BIAS: i32 = 127 + Self::MANTISSA_SIZE; + const DENORMAL_EXPONENT: i32 = 1 - Self::EXPONENT_BIAS; + const MAX_EXPONENT: i32 = 0xFF - Self::EXPONENT_BIAS; + + #[inline(always)] + fn to_bits(self) -> u32 { + f32::to_bits(self) + } + + #[inline(always)] + fn from_bits(u: u32) -> f32 { + f32::from_bits(u) + } + + #[inline(always)] + fn ln(self) -> f32 { + #[cfg(feature = "std")] + return f32::ln(self); + + #[cfg(not(feature = "std"))] + return libm::logf(self); + } + + #[inline(always)] + fn floor(self) -> f32 { + #[cfg(feature = "std")] + return f32::floor(self); + + #[cfg(not(feature = "std"))] + return libm::floorf(self); + } + + #[inline(always)] + fn is_sign_positive(self) -> bool { + f32::is_sign_positive(self) + } + + #[inline(always)] + fn is_sign_negative(self) -> bool { + f32::is_sign_negative(self) + } +} + +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] +impl Float for f64 { + type Unsigned = u64; + float_literals!(f64); + float_masks!( + float => Self, + sign_mask => 0x8000000000000000, + exponent_mask => 0x7FF0000000000000, + hidden_bit_mask => 0x0010000000000000, + mantissa_mask => 0x000FFFFFFFFFFFFF, + ); + const EXPONENT_SIZE: i32 = 11; + const MANTISSA_SIZE: i32 = 52; + const EXPONENT_BIAS: i32 = 1023 + Self::MANTISSA_SIZE; + const DENORMAL_EXPONENT: i32 = 1 - Self::EXPONENT_BIAS; + const MAX_EXPONENT: i32 = 0x7FF - Self::EXPONENT_BIAS; + + #[inline(always)] + fn to_bits(self) -> u64 { + f64::to_bits(self) + } + + #[inline(always)] + fn from_bits(u: u64) -> f64 { + f64::from_bits(u) + } + + #[inline(always)] + fn ln(self) -> f64 { + #[cfg(feature = "std")] + return f64::ln(self); + + #[cfg(not(feature = "std"))] + return libm::logd(self); + } + + #[inline(always)] + fn floor(self) -> f64 { + #[cfg(feature = "std")] + return f64::floor(self); + + #[cfg(not(feature = "std"))] + return libm::floord(self); + } + + #[inline(always)] + fn is_sign_positive(self) -> bool { + f64::is_sign_positive(self) + } + + #[inline(always)] + fn is_sign_negative(self) -> bool { + f64::is_sign_negative(self) + } +} + +// #[cfg(feature = "f128")] +// impl Float for f128 { +// type Unsigned = u128; +// float_literals!(f128); +// float_masks!( +// float => Self, +// sign_mask => 0x80000000000000000000000000000000, +// exponent_mask => 0x7FFF0000000000000000000000000000, +// hidden_bit_mask => 0x00010000000000000000000000000000, +// mantissa_mask => 0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFF, +// ); +// const EXPONENT_SIZE: i32 = 15; +// const MANTISSA_SIZE: i32 = 112; +// const EXPONENT_BIAS: i32 = 16383 + Self::MANTISSA_SIZE; +// const DENORMAL_EXPONENT: i32 = 1 - Self::EXPONENT_BIAS; +// const MAX_EXPONENT: i32 = 0x7FFF - Self::EXPONENT_BIAS; +// } diff --git a/rust/vendor/lexical-util/src/options.rs b/rust/vendor/lexical-util/src/options.rs new file mode 100644 index 0000000..e6f9e4b --- /dev/null +++ b/rust/vendor/lexical-util/src/options.rs @@ -0,0 +1,224 @@ +//! Shared traits for the options API. +//! +//! The following constants have the following signifiers: +//! +//! - `${X}_LITERAL`: Applies to all literal values for that language (for +//! example, [`RUST_LITERAL`]). +//! - `${X}_STRING`: Applies to all string values for that language (for +//! example, [`ERLANG_STRING`]). +//! - `${X}`: Applies to all values for that language (for example, [`KAWA`]). +//! - `${X}_(NAN|INF|INFINITY)`: Applies to only a single special value (for +//! example, [`PHP_LITERAL_NAN`], [`PHP_LITERAL_INF`], and +//! [`PHP_LITERAL_INFINITY`]). +//! +//! If it's not defined, all values are the default. The default options +//! are: +//! - NaN: (`*_NAN`): `NaN` +//! - Short infinity: (`*_INF`): `Inf` (including `+Inf` and `-Inf`) +//! - Long infinity: (`*_INFINITY`): `Infinity` (including `+Infinity` and +//! `-Infinity`) + +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +use crate::constants::FormattedSize; + +// TRAITS +// ------ + +#[doc(hidden)] +#[macro_export] +macro_rules! write_options_doc { + () => { + " +Get an upper bound on the required buffer size. + +
+ +This method is soft-deprecated and meant for internal use. +You should always use [`buffer_size_const`] so you can get +the required buffer size at compile time to determine the +buffer size required. + +
+ +[`buffer_size_const`]: Self::buffer_size_const + +This is used when custom formatting options, such as significant +digits specifiers or custom exponent breaks, are used, which +can lead to more or less significant digits being written than +expected. If using the default formatting options, then this will +always be [`FORMATTED_SIZE`][FormattedSize::FORMATTED_SIZE] or +[`FORMATTED_SIZE_DECIMAL`][FormattedSize::FORMATTED_SIZE_DECIMAL], +depending on the radix. +" + }; +} + +/// Shared trait for all writer options. +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +pub trait WriteOptions: Default { + /// Determine if the options are valid. + fn is_valid(&self) -> bool; + + /// Get an upper bound on the required buffer size. + /// + ///
+ /// + /// This method is soft-deprecated and meant for internal use. + /// You should always use `buffer_size_const` for either [`integer`] or + /// [`float`] writer so you can get the required buffer size at compile time + /// to determine the buffer size required. + /// + ///
+ /// + /// [`integer`]: https://docs.rs/lexical-write-integer/latest/lexical_write_integer/struct.Options.html#method.buffer_size_const + /// [`float`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/struct.Options.html#method.buffer_size_const + /// + /// This is used when custom formatting options, such as significant + /// digits specifiers or custom exponent breaks, are used, which + /// can lead to more or less significant digits being written than + /// expected. If using the default formatting options, then this will + /// always be [`FORMATTED_SIZE`][FormattedSize::FORMATTED_SIZE] or + /// [`FORMATTED_SIZE_DECIMAL`][FormattedSize::FORMATTED_SIZE_DECIMAL], + /// depending on the radix. + /// + /// Using `buffer_size_const` lets you create static arrays at compile time, + /// rather than dynamically-allocate memory or know the value ahead of time. + #[deprecated = "Use `buffer_size_const` instead. Will be removed in 2.0."] + fn buffer_size(&self) -> usize; +} + +/// Shared trait for all parser options. +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] +pub trait ParseOptions: Default { + /// Determine if the options are valid. + fn is_valid(&self) -> bool; +} + +// PRE-DEFINED CONSTANTS +// --------------------- + +// The following constants have the following signifiers: +// ${X}_LITERAL - Applies to all literal values for that language. +// ${X}_STRING - Applies to all string values for that language. +// ${X} - Applies to all values for that language. +// ${X}_(NAN|INF|INFINITY) - Applies to only a single special value. +// IF it's not defined, all values are the default. + +macro_rules! literal { + ($name:ident, $value:ident $(, $doc:literal)?) => { + $(#[doc = $doc])? + pub const $name: Option<&[u8]> = $value; + }; + ($name:ident, $value:literal $(, $doc:literal)?) => { + $(#[doc = $doc])? + pub const $name: Option<&[u8]> = Some($value); + }; +} + +literal!(RUST_LITERAL, None, "A `Rust` literal number (uses default options)."); +// RUST_STRING +literal!(PYTHON_LITERAL, None, "A `Python` literal number (uses default options)."); +// PYTHON_STRING +literal!(CXX_LITERAL_NAN, b"NAN", "A `C++` literal NaN (`NAN`)."); +literal!(CXX_LITERAL_INF, b"INFINITY", "A `C++` literal short infinity (`INFINITY`)."); +literal!(CXX_LITERAL_INFINITY, b"INFINITY", "A `C++` literal long infinity (`INFINITY`)."); +// CXX_STRING +literal!(C_LITERAL_NAN, b"NAN", "A `C` literal NaN (`NAN`)."); +literal!(C_LITERAL_INF, b"INFINITY", "A `C` literal short infinity (`INFINITY`)."); +literal!(C_LITERAL_INFINITY, b"INFINITY", "A `C` literal long infinity (`INFINITY`)."); +// RUBY_LITERAL +literal!(RUBY_LITERAL_NAN, b"NaN", "A `Ruby` literal NaN (`NaN`)."); +literal!(RUBY_LITERAL_INF, b"Infinity", "A `Ruby` literal short infinity (`Infinity`)."); +literal!(RUBY_STRING_NONE, None, "A `Ruby` string (uses default options)."); +// C_STRING +literal!(SWIFT_LITERAL, None, "A `Swift` literal number (uses default options)."); +// SWIFT_STRING +literal!(GO_LITERAL, None, "A `Golang` literal number (uses default options)."); +// GO_STRING +literal!(HASKELL_LITERAL, None, "A `Haskell` literal number (uses default options)."); +literal!(HASKELL_STRING_INF, b"Infinity", "A `Haskell` string short infinity (`Infinity`)."); +literal!(HASKELL_STRING_INFINITY, b"Infinity", "A `Haskell` string long infinity (`Infinity`)."); +literal!(JAVASCRIPT_INF, b"Infinity", "A `JavaScript` string short infinity (`Infinity`)."); +literal!(JAVASCRIPT_INFINITY, b"Infinity", "A `JavaScript` string long infinity (`Infinity`)."); +literal!(PERL_LITERAL, None, "A `Perl` literal literal (uses default options)."); +// PERL_STRING +literal!(PHP_LITERAL_NAN, b"NAN", "A `PHP` literal NaN (`NAN`)."); +literal!(PHP_LITERAL_INF, b"INF", "A `PHP` literal short infinity (`INF`)."); +literal!(PHP_LITERAL_INFINITY, b"INF", "A `PHP` literal long infinity (`INF`)."); +// PHP_STRING +literal!(JAVA_LITERAL, None, "A `Java` literal number (uses default options)."); +literal!(JAVA_STRING_INF, b"Infinity", "A `Java` string short infinity (`Infinity`)."); +literal!(JAVA_STRING_INFINITY, b"Infinity", "A `Java` string long infinity (`Infinity`)."); +literal!(R_LITERAL_INF, b"Inf", "An `R` literal short infinity (`Inf`)."); +literal!(R_LITERAL_INFINITY, b"Inf", "An `R` literal long infinity (`Inf`)."); +// R_STRING +literal!(KOTLIN_LITERAL, None, "A `Kotlin` literal number (uses default options)."); +literal!(KOTLIN_STRING_INF, b"Infinity", "A `Kotlin` string short infinity (`Infinity`)."); +literal!(KOTLIN_STRING_INFINITY, b"Infinity", "A `Kotlin` string long infinity (`Infinity`)."); +literal!(JULIA_LITERAL_INF, b"Inf", "A `Julia` string short infinity (`Inf`)."); +literal!(JULIA_LITERAL_INFINITY, b"Inf", "A `Julia` string long infinity (`Inf`)."); +// JULIA_STRING +literal!(CSHARP_LITERAL, None, "A `C#` literal number (uses default options)."); +literal!(CSHARP_STRING_INF, b"Infinity", "A `C#` string short infinity (`Infinity`)."); +literal!(CSHARP_STRING_INFINITY, b"Infinity", "A `C#` string long infinity (`Infinity`)."); +literal!(KAWA, None, "A `Kawa` (Lisp) literal number (uses default options)."); +literal!(GAMBITC, None, "A `Gambit-C` (Lisp) literal number (uses default options)."); +literal!(GUILE, None, "A `Guile` (Lisp) literal number (uses default options)."); +literal!(CLOJURE_LITERAL, None, "A `Clojure` (Lisp) literal number (uses default options)."); +literal!(CLOJURE_STRING_INF, b"Infinity", "A `Clojure` string short infinity (`Infinity`)."); +literal!(CLOJURE_STRING_INFINITY, b"Infinity", "A `Clojure` string long infinity (`Infinity`)."); +literal!(ERLANG_LITERAL_NAN, b"nan", "An `Erlang` literal NaN (`nan`)."); +literal!(ERLANG_STRING, None, "An `Erlang` string number (uses default options)."); +literal!(ELM_LITERAL, None, "An `Elm` literal number (uses default options)."); +literal!(ELM_STRING_NAN, None, "An `Elm` strong NaN (uses default options)."); +literal!(ELM_STRING_INF, b"Infinity", "An `Elm` string short infinity (`Infinity`)."); +literal!(ELM_STRING_INFINITY, b"Infinity", "An `Elm` string long infinity (`Infinity`)."); +literal!(SCALA_LITERAL, None, "A `Scala` literal number (uses default options)."); +literal!(SCALA_STRING_INF, b"Infinity", "A `Scala` string short infinity (`Infinity`)."); +literal!(SCALA_STRING_INFINITY, b"Infinity", "A `Scala` string long infinity (`Infinity`)."); +literal!(ELIXIR, None, "An `Elixir` number (uses default options)."); +literal!(FORTRAN_LITERAL, None, "A `FORTRAN` literal number (uses default options)."); +// FORTRAN_STRING +literal!(D_LITERAL, None, "A `D-Lang` literal number (uses default options)."); +// D_STRING +literal!(COFFEESCRIPT_INF, b"Infinity", "A `CoffeeScript` string short infinity (`Infinity`)."); +literal!(COFFEESCRIPT_INFINITY, b"Infinity", "A `CoffeeScript` string long infinity (`Infinity`)."); +literal!(COBOL, None, "A `COBOL` literal number (uses default options)."); +literal!(FSHARP_LITERAL_NAN, b"nan", "An `F#` literal NaN (`nan`)."); +literal!(FSHARP_LITERAL_INF, b"infinity", "An `F#` literal short infinity (`infinity`)."); +literal!(FSHARP_LITERAL_INFINITY, b"infinity", "An `F#` literal long infinity (`infinity`)."); +// FSHARP_STRING +literal!(VB_LITERAL, None, "A `Visual Basic` literal number (uses default options)"); +literal!(VB_STRING_INF, None, "A `Visual Basic` short string infinity (uses default options)"); +literal!(VB_STRING_INFINITY, None, "A `Visual Basic` long string number (uses default options)"); +literal!(OCAML_LITERAL_NAN, b"nan", "An `OCAML` literal NaN (`nan`)."); +literal!(OCAML_LITERAL_INF, b"infinity", "An `OCAML` literal short infinity (`infinity`)."); +literal!(OCAML_LITERAL_INFINITY, b"infinity", "An `OCAML` literal long infinity (`infinity`)."); +// OCAML_STRING +literal!(OBJECTIVEC, None, "An `Objective-C` number (uses default options)."); +literal!(REASONML_LITERAL_NAN, b"nan", "A `ReasonML` literal NaN (`nan`)."); +literal!(REASONML_LITERAL_INF, b"infinity", "A `ReasonML` literal short infinity (`infinity`)."); +literal!( + REASONML_LITERAL_INFINITY, + b"infinity", + "A `ReasonML` literal long infinity (`infinity`)." +); +// REASONML_STRING +literal!(MATLAB_LITERAL_INF, b"inf", "A `MATLAB` literal short infinity (`inf`)."); +literal!(MATLAB_LITERAL_INFINITY, b"Inf", "A `MATLAB` literal long infinity (`Inf`)."); +// MATLAB_STRING +literal!(ZIG_LITERAL, None, "A `Zig` literal number (uses default options)."); +// ZIG_STRING +literal!(SAGE_LITERAL_INF, b"infinity", "A `SageMath` literal short infinity (`infinity`)."); +literal!(SAGE_LITERAL_INFINITY, b"Infinity", "A `SageMath` literal long infinity (`Infinity`)."); +// SAGE_STRING +literal!(JSON, None, "A `JSON` number (uses default options)."); +literal!(TOML, None, "A `TOML` number (uses default options)."); +literal!(YAML, None, "A `YAML` number (uses default options)."); +literal!(XML_INF, None, "An `XML` short infinity (uses default options)."); +literal!(XML_INFINITY, None, "An `XML` short infinity (uses default options)."); +literal!(SQLITE, None, "A `SQLite` number (uses default options)."); +literal!(POSTGRESQL, None, "A `PostgreSQL` number (uses default options)."); +literal!(MYSQL, None, "A `MySQL` number (uses default options)."); +literal!(MONGODB_INF, b"Infinity", "A `MongoDB` short infinity (`Infinity`)."); +literal!(MONGODB_INFINITY, b"Infinity", "A `MongoDB` long infinity (`Infinity`)."); diff --git a/rust/vendor/lexical-util/src/prebuilt_formats.rs b/rust/vendor/lexical-util/src/prebuilt_formats.rs new file mode 100644 index 0000000..287248d --- /dev/null +++ b/rust/vendor/lexical-util/src/prebuilt_formats.rs @@ -0,0 +1,2173 @@ +//! Pre-built formats for each programming language, + +#![cfg(feature = "format")] + +use core::num; + +use crate::format::NumberFormatBuilder; + +// FIXME + +// Sample test code for each language used: +// +// Rust +// ---- +// +// Setup: +// Save to `main.rs` and run `rustc main.rs -o main`. +// +// Code: +// ```text +// pub fn main() { +// println!("{:?}", 3_.0f32); +// println!("{:?}", "3_.0".parse::()); +// } +// ``` +// +// Python +// ------ +// +// Setup: +// Run `python` to enter the interpreter. +// +// Code: +// ```text +// print(3_.0) +// print(float("3_.0")) +// ``` +// +// C++ +// --- +// +// Setup: +// Save to `main.cc` and run `g++ main.cc -o main -std=c++XX`, +// where XX is one of the following values: +// - 98 +// - 03 +// - 11 +// - 14 +// - 17 +// +// Code: +// ```text +// #include +// #include +// #include +// #include +// #include +// +// double parse(const char* string) { +// char* end; +// double result = strtod(string, &end); +// auto endp = reinterpret_cast(end); +// if (std::distance(string, endp) != strlen(string)) { +// throw std::invalid_argument("did not consume entire string."); +// } +// return result; +// } +// +// int main() { +// std::cout << 3'.0 << std::endl; +// std::cout << parse("3'.0") << std::endl; +// } +// ``` +// +// C +// - +// +// Setup: +// Save to `main.c` and run `gcc main.c -o main -std=cXX`, +// where XX is one of the following values: +// - 89 +// - 90 +// - 99 +// - 11 +// - 18 +// +// Code: +// ```text +// #include +// #include +// #include +// #include +// +// size_t distance(const char* first, const char* last) { +// uintptr_t x = (uintptr_t) first; +// uintptr_t y = (uintptr_t) last; +// return (size_t) (y - x); +// } +// +// double parse(const char* string) { +// char* end; +// double result = strtod(string, &end); +// if (distance(string, (const char*) end) != strlen(string)) { +// abort(); +// } +// return result; +// } +// +// int main() { +// printf("%f\n", 3'.); +// printf("%f\n", parse("3'.")); +// } +// ``` +// +// Ruby +// ---- +// +// Setup: +// Run `irb` to enter the interpreter. +// +// Code: +// ```text +// puts 3.0_1; +// puts "3.0_1".to_f; +// ``` +// Swift +// ----- +// +// Setup: +// Run `swift` to enter the interpreter. +// +// Code: +// ```text +// print(3.0); +// print(Float("3.0")); +// ``` +// Golang +// ------ +// +// Setup: +// Save to `main.go` and run `go run main.go` +// +// Code: +// ```text +// package main +// +// import ( +// "fmt" +// "strconv" +// ) +// +// func main() { +// fmt.Println(3.0) +// fmt.Println(strconv.ParseFloat("3.0", 64)) +// } +// ``` +// +// Haskell +// ------- +// +// Setup: +// Run `ghci` to enter the interpreter. +// +// Code: +// ```text +// :m Numeric +// showFloat 3.0 "" +// let x = "3.0" +// read x :: Float +// ``` +// +// Javascript +// ---------- +// +// Setup: +// Run `nodejs` (or `node`) to enter the interpreter. +// +// Code: +// ```text +// console.log(3.0) +// console.log(parseFloat("3.0")) +// ``` +// +// Perl +// ---- +// +// Setup: +// Run `perl -de1` to enter the interpret. +// +// Code: +// ```text +// print 3.01; +// print '3.01' * 1; +// ``` +// +// PHP +// --- +// +// Setup: +// Run `php -a` to enter the interpret. +// +// Code: +// ```text +// printf("%f\n", 3.0); +// printf("%f\n", floatval("3.0")); +// ``` +// +// Java +// ---- +// +// Setup: +// Save to `main.java` and run `javac main.java`, then run `java Main`. +// +// Code: +// ```text +// class Main { +// public static void main(String args[]) { +// System.out.println(3.0); +// System.out.println(Float.parseFloat("3.0")); +// } +// } +// ``` +// +// R +// - +// +// Setup: +// Run `R` to enter the interpret. +// +// Code: +// ```text +// print(3.0); +// print(as.numeric("3.0")); +// ``` +// +// Kotlin +// ------ +// +// Setup: +// Save file to `main.kt` and run `kotlinc main.kt -d main.jar`, +// then run `java -jar main.jar`. +// +// Code: +// ```text +// fun main() { +// println(3.0) +// println("3.0".toDouble()) +// } +// ``` +// +// Julia +// ----- +// +// Setup: +// Run `julia` to enter the interpret. +// +// Code: +// ```text +// print(3.0); +// print(parse(Float64, "3.0")); +// ``` +// +// C# +// -- +// +// Note: +// Mono accepts both integer and fraction decimal separators, Mono is +// just buggy, see https://github.com/dotnet/csharplang/issues/55#issuecomment-574902516. +// +// Setup: +// Run `csharp -langversion:X` to enter the interpret, +// where XX is one of the following values: +// - ISO-1 +// - ISO-2 +// - 3 +// - 4 +// - 5 +// - 6 +// - 7 +// +// Code: +// ```text +// Console.WriteLine("{0}", 3.0); +// Console.WriteLine("{0}", float.Parse("3.0")); +// ``` +// +// Kawa +// ---- +// +// Setup: +// Run `kawa` to enter the interpreter. +// +// Code: +// ```text +// 3.0 +// (string->number "3.0") +// ``` +// +// Gambit-C +// -------- +// +// Setup: +// Run `gsc` to enter the interpreter. +// +// Code: +// ```text +// 3.0 +// (string->number "3.0") +// ``` +// +// Guile +// ----- +// +// Setup: +// Run `guile` to enter the interpreter. +// +// Code: +// ```text +// 3.0 +// (string->number "3.0") +// ``` +// +// Clojure +// ------- +// +// Setup: +// Run `clojure` to enter the interpreter. +// +// Code: +// ```text +// 3.0 +// (Float/parseFloat "3.0") +// ``` +// +// Erlang +// ------ +// +// Setup: +// Run `erl` to enter the interpreter. +// +// Code: +// ```text +// io:format("~p~n", [3.0]). +// string:to_float("3.0"). +// ``` +// +// Elm +// --- +// +// Setup: +// Run `elm repl` to enter the interpreter. +// +// Code: +// ```text +// 3.0 +// String.toFloat "3.0" +// ``` +// +// Scala +// ----- +// +// Setup: +// Run `scala` to enter the interpreter. +// +// Code: +// ```text +// 3.0 +// "3.0".toFloat +// ``` +// +// Elixir +// ------ +// +// Setup: +// Run `iex` to enter the interpreter. +// +// Code: +// ```text +// 3.0; +// String.to_float("3.0"); +// ``` +// +// FORTRAN +// ------- +// +// Setup: +// Save to `main.f90` and run `gfortran -o main main.f90` +// +// Code: +// ```text +// program main +// real :: x +// character (len=30) :: word +// word = "3." +// read(word, *) x +// print *, 3. +// print *, x +// end program main +// ``` +// +// D +// - +// +// Setup: +// Save to `main.d` and run `dmd -run main.d` +// +// Code: +// ```text +// import std.conv; +// import std.stdio; +// +// void main() +// { +// writeln(3.0); +// writeln(to!double("3.0")); +// } +// ``` +// +// Coffeescript +// ------------ +// +// Setup: +// Run `coffee` to enter the interpreter. +// +// Code: +// ```text +// 3.0; +// parseFloat("3.0"); +// ``` +// +// Cobol +// ----- +// +// Setup: +// Save to `main.cbl` and run `cobc main.cbl` then `cobcrun main`. +// +// Code: +// ```text +// IDENTIFICATION DIVISION. +// PROGRAM-ID. main. +// +// DATA DIVISION. +// WORKING-STORAGE SECTION. +// 01 R PIC X(20) VALUE "3.0". +// 01 TOTAL USAGE IS COMP-2. +// +// PROCEDURE DIVISION. +// COMPUTE TOTAL = FUNCTION NUMVAL(R). +// Display 3.0. +// Display TOTAL. +// STOP RUN. +// ``` +// +// F# +// -- +// +// Setup: +// Run `dotnet fsi` to enter the interpreter. +// +// Code: +// ```text +// printfn "%f" 3.0;; +// let f = float "3.0";; +// printfn "%f" f;; +// ``` +// +// Visual Basic +// ------------ +// +// Setup: +// Save to `main.vb` and run `vbnc main.vb`. +// +// Code: +// ```text +// Imports System +// +// Module Module1 +// Sub Main() +// Console.WriteLine(Format$(3.0, "0.0000000000000")) +// Console.WriteLine(Format$(CDbl("3.0"), "0.0000000000000")) +// End Sub +// End Module +// ``` +// +// OCaml +// ----- +// +// Setup: +// Save to `main.ml` and run `ocamlc -o main main.ml`. +// +// Code: +// ```text +// Printf.printf "%f\n" 3.0 +// let () = +// let f = float_of_string "3.0" in +// Printf.printf "%f\n" f +// ``` +// +// Objective-C +// ----------- +// +// Setup: +// Save to `main.m` and run `gcc -o main -lobjc -lgnustep-base main.m +// -fconstant-string-class=NSConstantString`. +// +// Code: +// ```text +// #import +// #import +// +// int main(int argv, char* argc[]) +// { +// printf("%f\n", 3.0); +// NSString *s = @"3.0"; +// double f = [s doubleValue]; +// printf("%f\n", f); +// } +// ``` +// +// ReasonML +// -------- +// +// Setup: +// Run `rtop` to enter the interpreter. +// +// Code: +// ```text +// Printf.printf("%f\n", 3.0); +// Printf.printf("%f\n", float_of_string("3.0")); +// ``` +// +// Zig +// --- +// +// Setup: +// Save to `main.zig` and run `zig build-exe main.zig` +// +// Code: +// ```text +// const std = @import("std"); +// +// pub fn main() void { +// const f: f64 = 3.0; +// std.debug.warn("{}\n", f); +// const x: f64 = std.fmt.parseFloat(f64, "3.0") catch unreachable; +// std.debug.warn("{}\n", x); +// } +// ``` +// +// +// Octave (and Matlab) +// ------------------- +// +// Setup: +// Run `octave` to enter the interpreter, or +// run `octave --traditional` to enter the Matlab interpret. +// +// Code: +// ```text +// 3.0 +// str2double("3.0") +// ``` +// +// Sage +// ---- +// +// Setup: +// Run `sage` to enter the interpreter. +// +// Code: +// ```text +// 3.0 +// float("3.0") +// ``` +// +// JSON +// ---- +// +// Setup: +// Run `node` (or `nodejs`) to enter the JS interpreter. +// +// Code: +// ```text +// JSON.parse("3.0") +// ``` +// +// TOML +// ---- +// +// Setup: +// Run `python` to enter the Python interpreter. +// +// Code: +// ```text +// import tomlkit +// tomlkit.parse("a = 3.0") +// ``` +// +// XML +// --- +// +// Setup: +// Run `python` to enter the Python interpreter. +// +// Code: +// ```text +// from lxml import etree +// +// def validate_xml(xsd, xml): +// '''Validate XML file against schema''' +// +// schema = etree.fromstring(xsd) +// doc = etree.fromstring(xml) +// xmlschema = etree.XMLSchema(schema) +// +// return xmlschema.validate(doc) +// +// +// xsd = b''' +// +// +// ''' +// +// xml = b''' +// 3.0 +// ''' +// +// validate_xml(xsd, xml) +// ``` +// +// SQLite +// ------ +// +// Setup: +// Run `sqlite3 :memory:` to enter the sqlite3 interpreter +// with an in-memory database. +// +// Code: +// ```text +// CREATE TABLE stocks (price real); +// INSERT INTO stocks VALUES (3.0); +// SELECT * FROM stocks; +// ``` +// +// PostgreSQL +// ---------- +// +// Setup: +// Run `initdb -D db` to create a database data direction, +// then run `pg_ctl -D db start` to start the server, then run +// `createdb` to create a user database and `psql` to start the +// interpreter. +// +// Code: +// ```text +// CREATE TABLE stocks (price real); +// INSERT INTO stocks VALUES (3.0); +// SELECT * FROM stocks; +// ``` +// +// MySQL +// ----- +// +// Setup: +// Run `mysqld` to start the server, then run `mysql` to start the +// interpreter. +// +// Code: +// ```text +// USE mysql; +// CREATE TABLE stocks (price real); +// INSERT INTO stocks VALUES (3.0); +// SELECT * FROM stocks; +// ``` +// +// MongoDB +// ------- +// +// Setup: +// Run `mongod --dbpath data/db` to start the server, then run +// `mongo` to start the interpreter. +// +// Code: +// ```text +// use mydb +// db.movie.insert({"name": 3.0}) +// db.movie.find() +// ``` + +// PRE-DEFINED CONSTANTS +// --------------------- +// +// Sample Format Shorthand: +// ------------------------ +// +// The format shorthand lists the test cases, and if applicable, +// the digit separator character. For example, the shorthand +// `[134-_]` specifies it passes tests 1, 3, and 4, and uses +// `'_'` as a digit-separator character. Meanwhile, `[0]` means it +// passes test 0, and has no digit separator. + +// RUST LITERAL [4569ABFGHIJKMN-_] +/// Number format for a [`Rust`] literal floating-point number. +/// +/// [`Rust`]: https://www.rust-lang.org/ +#[rustfmt::skip] +pub const RUST_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .required_digits(true) + .no_positive_mantissa_sign(true) + .no_special(true) + .internal_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// RUST STRING [0134567MN] +/// Number format to parse a [`Rust`] float from string. +/// +/// [`Rust`]: https://www.rust-lang.org/ +#[rustfmt::skip] +pub const RUST_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +/// Number format for a [`Python`] literal floating-point number. +/// +/// [`Python`]: https://www.python.org/ +pub const PYTHON_LITERAL: u128 = PYTHON3_LITERAL; + +/// Number format to parse a [`Python`] float from string. +/// +/// [`Python`]: https://www.python.org/ +pub const PYTHON_STRING: u128 = PYTHON3_STRING; + +/// Number format for a [`Python3`] literal floating-point number. +/// +/// [`Python3`]: https://www.python.org/ +pub const PYTHON3_LITERAL: u128 = PYTHON36_LITERAL; + +// PYTHON3 STRING [0134567MN] +/// Number format to parse a [`Python3`] float from string. +/// +/// [`Python3`]: https://www.python.org/ +#[rustfmt::skip] +pub const PYTHON3_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// PYTHON3.6+ LITERAL [013456N-_] +/// Number format for a [`Python3.6`] or higher literal floating-point number. +/// +/// [`Python3.6`]: https://www.python.org/downloads/release/python-360/ +#[rustfmt::skip] +pub const PYTHON36_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .no_special(true) + .no_integer_leading_zeros(true) + .internal_digit_separator(true) + .build_strict(); + +// PYTHON3.5- LITERAL [013456N] +/// Number format for a [`Python3.5`] or lower literal floating-point number. +/// +/// [`Python3.5`]: https://www.python.org/downloads/release/python-350/ +#[rustfmt::skip] +pub const PYTHON35_LITERAL: u128 = NumberFormatBuilder::new() + .no_special(true) + .no_integer_leading_zeros(true) + .build_strict(); + +// PYTHON2 LITERAL [013456MN] +/// Number format for a [`Python2`] literal floating-point number. +/// +/// [`Python2`]: https://www.python.org/downloads/release/python-270/ +#[rustfmt::skip] +pub const PYTHON2_LITERAL: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// PYTHON2 STRING [0134567MN] +/// Number format to parse a [`Python2`] float from string. +/// +/// [`Python2`]: https://www.python.org/downloads/release/python-270/ +#[rustfmt::skip] +pub const PYTHON2_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +/// Number format for a [`C++`] literal floating-point number. +/// +/// [`C++`]: https://en.cppreference.com/w/ +pub const CXX_LITERAL: u128 = CXX20_LITERAL; + +/// Number format to parse a [`C++`] float from string. +/// +/// [`C++`]: https://en.cppreference.com/w/ +pub const CXX_STRING: u128 = CXX20_STRING; + +/// Number format for a [`C++`] literal hexadecimal floating-point number. +/// +/// [`C++`]: https://en.cppreference.com/w/ +#[cfg(feature = "power-of-two")] +pub const CXX_HEX_LITERAL: u128 = CXX20_HEX_LITERAL; + +/// Number format to parse a [`C++`] hexadecimal float from string. +/// +/// [`C++`]: https://en.cppreference.com/w/ +#[cfg(feature = "power-of-two")] +pub const CXX_HEX_STRING: u128 = CXX20_HEX_STRING; + +// C++20 LITERAL [013456789ABMN-'] +/// Number format for a [`C++20`] literal floating-point number. +/// +/// [`C++20`]: https://en.cppreference.com/w/cpp/20 +#[rustfmt::skip] +pub const CXX20_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'\'')) + .case_sensitive_special(true) + .internal_digit_separator(true) + .build_strict(); + +// C++20 STRING [0134567MN] +/// Number format for a [`C++20`] string floating-point number. +/// +/// [`C++20`]: https://en.cppreference.com/w/cpp/20 +#[rustfmt::skip] +pub const CXX20_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// C++20 HEX LITERAL [013456789ABMN-'] +/// Number format for a [`C++20`] literal hexadecimal floating-point number. +/// +/// [`C++20`]: https://en.cppreference.com/w/cpp/20 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const CXX20_HEX_LITERAL: u128 = NumberFormatBuilder::new() + .required_exponent_notation(true) + .digit_separator(num::NonZeroU8::new(b'\'')) + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .case_sensitive_special(true) + .internal_digit_separator(true) + .build_strict(); + +// C++20 HEX STRING [0134567MN] +/// Number format for a [`C++20`] string hexadecimal floating-point number. +/// +/// [`C++20`]: https://en.cppreference.com/w/cpp/20 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const CXX20_HEX_STRING: u128 = NumberFormatBuilder::new() + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// C++17 LITERAL [013456789ABMN-'] +/// Number format for a [`C++17`] literal floating-point number. +/// +/// [`C++17`]: https://en.cppreference.com/w/cpp/17 +#[rustfmt::skip] +pub const CXX17_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'\'')) + .case_sensitive_special(true) + .internal_digit_separator(true) + .build_strict(); + +// C++17 STRING [0134567MN] +/// Number format for a [`C++17`] string floating-point number. +/// +/// [`C++17`]: https://en.cppreference.com/w/cpp/17 +#[rustfmt::skip] +pub const CXX17_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// C++17 HEX LITERAL [013456789ABMN-'] +/// Number format for a [`C++17`] literal hexadecimal floating-point number. +/// +/// [`C++17`]: https://en.cppreference.com/w/cpp/17 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const CXX17_HEX_LITERAL: u128 = NumberFormatBuilder::new() + .required_exponent_notation(true) + .digit_separator(num::NonZeroU8::new(b'\'')) + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .case_sensitive_special(true) + .internal_digit_separator(true) + .build_strict(); + +// C++17 HEX STRING [0134567MN] +/// Number format for a [`C++17`] string hexadecimal floating-point number. +/// +/// [`C++17`]: https://en.cppreference.com/w/cpp/17 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const CXX17_HEX_STRING: u128 = NumberFormatBuilder::new() + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// C++14 LITERAL [013456789ABMN-'] +/// Number format for a [`C++14`] literal floating-point number. +/// +/// [`C++14`]: https://en.cppreference.com/w/cpp/14 +#[rustfmt::skip] +pub const CXX14_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'\'')) + .case_sensitive_special(true) + .internal_digit_separator(true) + .build_strict(); + +// C++14 STRING [0134567MN] +/// Number format for a [`C++14`] string floating-point number. +/// +/// [`C++14`]: https://en.cppreference.com/w/cpp/14 +#[rustfmt::skip] +pub const CXX14_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// C++14 HEX STRING [0134567MN] +/// Number format for a [`C++14`] string hexadecimal floating-point number. +/// +/// [`C++14`]: https://en.cppreference.com/w/cpp/14 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const CXX14_HEX_STRING: u128 = NumberFormatBuilder::new() + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// C++11 LITERAL [01345678MN] +/// Number format for a [`C++11`] literal floating-point number. +/// +/// [`C++11`]: https://en.cppreference.com/w/cpp/11 +#[rustfmt::skip] +pub const CXX11_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// C++11 STRING [0134567MN] +/// Number format for a [`C++11`] string floating-point number. +/// +/// [`C++11`]: https://en.cppreference.com/w/cpp/11 +#[rustfmt::skip] +pub const CXX11_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// C++11 HEX STRING [0134567MN] +/// Number format for a [`C++11`] string hexadecimal floating-point number. +/// +/// [`C++11`]: https://en.cppreference.com/w/cpp/11 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const CXX11_HEX_STRING: u128 = NumberFormatBuilder::new() + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// C++03 LITERAL [01345678MN] +/// Number format for a [`C++03`] literal floating-point number. +/// +/// [`C++03`]: https://en.wikipedia.org/wiki/C%2B%2B03 +#[rustfmt::skip] +pub const CXX03_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// C++03 STRING [0134567MN] +/// Number format for a [`C++03`] string floating-point number. +/// +/// [`C++03`]: https://en.wikipedia.org/wiki/C%2B%2B03 +#[rustfmt::skip] +pub const CXX03_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// C++98 LITERAL [01345678MN] +/// Number format for a [`C++98`] literal floating-point number. +/// +/// [`C++98`]: https://en.cppreference.com/w/ +#[rustfmt::skip] +pub const CXX98_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// C++98 STRING [0134567MN] +/// Number format for a [`C++98`] string floating-point number. +/// +/// [`C++98`]: https://en.cppreference.com/w/ +#[rustfmt::skip] +pub const CXX98_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +/// Number format for a [`C`] literal floating-point number. +/// +/// [`C`]: https://en.cppreference.com/w/c +pub const C_LITERAL: u128 = C18_LITERAL; + +/// Number format to parse a [`C`] float from string. +/// +/// [`C`]: https://en.cppreference.com/w/c +pub const C_STRING: u128 = C18_STRING; + +/// Number format for a [`C`] literal hexadecimal floating-point number. +/// +/// [`C`]: https://en.cppreference.com/w/c +#[cfg(feature = "power-of-two")] +pub const C_HEX_LITERAL: u128 = C18_HEX_LITERAL; + +/// Number format to parse a [`C`] hexadecimal float from string. +/// +/// [`C`]: https://en.cppreference.com/w/c +#[cfg(feature = "power-of-two")] +pub const C_HEX_STRING: u128 = C18_HEX_STRING; + +// C18 LITERAL [01345678MN] +/// Number format for a [`C18`] literal floating-point number. +/// +/// [`C18`]: https://en.cppreference.com/w/c/17 +#[rustfmt::skip] +pub const C18_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// C18 STRING [0134567MN] +/// Number format for a [`C18`] string floating-point number. +/// +/// [`C18`]: https://en.cppreference.com/w/c/17 +#[rustfmt::skip] +pub const C18_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// C18 HEX LITERAL [01345678MN] +/// Number format for a [`C18`] literal hexadecimal floating-point number. +/// +/// [`C18`]: https://en.cppreference.com/w/c/17 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const C18_HEX_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .required_exponent_notation(true) + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// C18 HEX STRING [0134567MN] +/// Number format for a [`C18`] string hexadecimal floating-point number. +/// +/// [`C18`]: https://en.cppreference.com/w/c/17 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const C18_HEX_STRING: u128 = NumberFormatBuilder::new() + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// C11 LITERAL [01345678MN] +/// Number format for a [`C11`] literal floating-point number. +/// +/// [`C11`]: https://en.cppreference.com/w/c/11 +#[rustfmt::skip] +pub const C11_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// C11 STRING [0134567MN] +/// Number format for a [`C11`] string floating-point number. +/// +/// [`C11`]: https://en.cppreference.com/w/c/11 +#[rustfmt::skip] +pub const C11_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// C11 HEX LITERAL [01345678MN] +/// Number format for a [`C11`] literal hexadecimal floating-point number. +/// +/// [`C11`]: https://en.cppreference.com/w/c/11 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const C11_HEX_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .required_exponent_notation(true) + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// C11 HEX STRING [0134567MN] +/// Number format for a [`C11`] string hexadecimal floating-point number. +/// +/// [`C11`]: https://en.cppreference.com/w/c/11 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const C11_HEX_STRING: u128 = NumberFormatBuilder::new() + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// C99 LITERAL [01345678MN] +/// Number format for a [`C99`] literal floating-point number. +/// +/// [`C99`]: https://en.cppreference.com/w/c/99 +#[rustfmt::skip] +pub const C99_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// C99 STRING [0134567MN] +/// Number format for a [`C99`] string floating-point number. +/// +/// [`C99`]: https://en.cppreference.com/w/c/99 +#[rustfmt::skip] +pub const C99_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// C99 HEX LITERAL [01345678MN] +/// Number format for a [`C99`] literal hexadecimal floating-point number. +/// +/// [`C99`]: https://en.cppreference.com/w/c/99 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const C99_HEX_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .required_exponent_notation(true) + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// C99 HEX STRING [0134567MN] +/// Number format for a [`C99`] string hexadecimal floating-point number. +/// +/// [`C99`]: https://en.cppreference.com/w/c/99 +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const C99_HEX_STRING: u128 = NumberFormatBuilder::new() + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// C90 LITERAL [013456MN] +/// Number format for a [`C90`] literal floating-point number. +/// +/// [`C90`]: https://en.cppreference.com/w/c +#[rustfmt::skip] +pub const C90_LITERAL: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// C90 STRING [0134567MN] +/// Number format for a [`C90`] string floating-point number. +/// +/// [`C90`]: https://en.cppreference.com/w/c +#[rustfmt::skip] +pub const C90_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// C90 HEX STRING [0134567MN] +/// Number format for a [`C90`] string hexadecimal floating-point number. +/// +/// [`C90`]: https://en.cppreference.com/w/c +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const C90_HEX_STRING: u128 = NumberFormatBuilder::new() + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// C89 LITERAL [013456MN] +/// Number format for a [`C89`] literal floating-point number. +/// +/// [`C89`]: https://en.cppreference.com/w/c +#[rustfmt::skip] +pub const C89_LITERAL: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// C89 STRING [0134567MN] +/// Number format for a [`C89`] string floating-point number. +/// +/// [`C89`]: https://en.cppreference.com/w/c +#[rustfmt::skip] +pub const C89_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// C89 HEX STRING [0134567MN] +/// Number format for a [`C89`] string hexadecimal floating-point number. +/// +/// [`C89`]: https://en.cppreference.com/w/c +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const C89_HEX_STRING: u128 = NumberFormatBuilder::new() + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +// RUBY LITERAL [345689AMN-_] +/// Number format for a [`Ruby`] literal floating-point number. +/// +/// [`Ruby`]: https://www.ruby-lang.org/en/ +#[rustfmt::skip] +pub const RUBY_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .required_exponent_sign(true) + .required_digits(true) + .no_special(true) + .no_integer_leading_zeros(true) + .no_float_leading_zeros(true) + .internal_digit_separator(true) + .build_strict(); + +// RUBY OCTAL LITERAL [345689AN-_] +/// Number format for an octal [`Ruby`] literal floating-point number. +/// +/// [`Ruby`]: https://www.ruby-lang.org/en/ +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const RUBY_OCTAL_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .mantissa_radix(8) + .required_digits(true) + .no_special(true) + .internal_digit_separator(true) + .build_strict(); + +// RUBY STRING [01234569ABMN-_] +// Note: Amazingly, Ruby 1.8+ do not allow parsing special values. +/// Number format to parse a [`Ruby`] float from string. +/// +/// [`Ruby`]: https://www.ruby-lang.org/en/ +#[rustfmt::skip] +pub const RUBY_STRING: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .no_special(true) + .internal_digit_separator(true) + .build_strict(); + +// SWIFT LITERAL [34569ABFGHIJKMN-_] +/// Number format for a [`Swift`] literal floating-point number. +/// +/// [`Swift`]: https://developer.apple.com/swift/ +#[rustfmt::skip] +pub const SWIFT_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .required_digits(true) + .no_special(true) + .internal_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// SWIFT STRING [134567MN] +/// Number format to parse a [`Swift`] float from string. +/// +/// [`Swift`]: https://developer.apple.com/swift/ +#[rustfmt::skip] +pub const SWIFT_STRING: u128 = NumberFormatBuilder::new() + .required_fraction_digits(true) + .build_strict(); + +// GO LITERAL [13456MN] +/// Number format for a [`Golang`] literal floating-point number. +/// +/// [`Golang`]: https://go.dev/ +#[rustfmt::skip] +pub const GO_LITERAL: u128 = NumberFormatBuilder::new() + .required_fraction_digits(true) + .no_special(true) + .build_strict(); + +// GO STRING [134567MN] +/// Number format to parse a [`Golang`] float from string. +/// +/// [`Golang`]: https://go.dev/ +#[rustfmt::skip] +pub const GO_STRING: u128 = NumberFormatBuilder::new() + .required_fraction_digits(true) + .build_strict(); + +// HASKELL LITERAL [456MN] +/// Number format for a [`Haskell`] literal floating-point number. +/// +/// [`Haskell`]: https://www.haskell.org/ +#[rustfmt::skip] +pub const HASKELL_LITERAL: u128 = NumberFormatBuilder::new() + .required_digits(true) + .no_positive_mantissa_sign(true) + .no_special(true) + .build_strict(); + +// HASKELL STRING [45678MN] +/// Number format to parse a [`Haskell`] float from string. +/// +/// [`Haskell`]: https://www.haskell.org/ +#[rustfmt::skip] +pub const HASKELL_STRING: u128 = NumberFormatBuilder::new() + .required_digits(true) + .no_positive_mantissa_sign(true) + .case_sensitive_special(true) + .build_strict(); + +// JAVASCRIPT LITERAL [01345678M] +/// Number format for a [`Javascript`] literal floating-point number. +/// +/// [`Javascript`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript +#[rustfmt::skip] +pub const JAVASCRIPT_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .no_float_leading_zeros(true) + .build_strict(); + +// JAVASCRIPT STRING [012345678MN] +/// Number format to parse a [`Javascript`] float from string. +/// +/// [`Javascript`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript +#[rustfmt::skip] +pub const JAVASCRIPT_STRING: u128 = NumberFormatBuilder::new() + .required_exponent_digits(false) + .case_sensitive_special(true) + .build_strict(); + +// PERL LITERAL [0134569ABDEFGHIJKMN-_] +/// Number format for a [`Perl`] literal floating-point number. +/// +/// [`Perl`]: https://www.perl.org/ +#[rustfmt::skip] +pub const PERL_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .no_special(true) + .internal_digit_separator(true) + .fraction_leading_digit_separator(true) + .exponent_leading_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// PERL STRING [01234567MN] +/// Number format to parse a [`Perl`] float from string. +/// +/// [`Perl`]: https://www.perl.org/ +pub const PERL_STRING: u128 = PERMISSIVE; + +// PHP LITERAL [01345678MN] +/// Number format for a [`PHP`] literal floating-point number. +/// +/// [`PHP`]: https://www.php.net/ +#[rustfmt::skip] +pub const PHP_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// PHP STRING [0123456MN] +/// Number format to parse a [`PHP`] float from string. +/// +/// [`PHP`]: https://www.php.net/ +#[rustfmt::skip] +pub const PHP_STRING: u128 = NumberFormatBuilder::new() + .required_exponent_digits(false) + .no_special(true) + .build_strict(); + +// JAVA LITERAL [0134569ABIJKMN-_] +/// Number format for a [`Java`] literal floating-point number. +/// +/// [`Java`]: https://www.java.com/en/ +#[rustfmt::skip] +pub const JAVA_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .no_special(true) + .internal_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// JAVA STRING [01345678MN] +/// Number format to parse a [`Java`] float from string. +/// +/// [`Java`]: https://www.java.com/en/ +#[rustfmt::skip] +pub const JAVA_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// R LITERAL [01345678MN] +/// Number format for an [`R`] literal floating-point number. +/// +/// [`R`]: https://www.r-project.org/ +#[rustfmt::skip] +pub const R_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// R STRING [01234567MN] +/// Number format to parse an [`R`] float from string. +/// +/// [`R`]: https://www.r-project.org/ +pub const R_STRING: u128 = PERMISSIVE; + +// KOTLIN LITERAL [0134569ABIJKN-_] +/// Number format for a [`Kotlin`] literal floating-point number. +/// +/// [`Kotlin`]: https://kotlinlang.org/ +#[rustfmt::skip] +pub const KOTLIN_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .no_special(true) + .no_integer_leading_zeros(true) + .internal_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// KOTLIN STRING [0134568MN] +/// Number format to parse a [`Kotlin`] float from string. +/// +/// [`Kotlin`]: https://kotlinlang.org/ +#[rustfmt::skip] +pub const KOTLIN_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// JULIA LITERAL [01345689AMN-_] +/// Number format for a [`Julia`] literal floating-point number. +/// +/// [`Julia`]: https://julialang.org/ +#[rustfmt::skip] +pub const JULIA_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .case_sensitive_special(true) + .integer_internal_digit_separator(true) + .fraction_internal_digit_separator(true) + .build_strict(); + +// JULIA STRING [01345678MN] +/// Number format to parse a [`Julia`] float from string. +/// +/// [`Julia`]: https://julialang.org/ +#[rustfmt::skip] +pub const JULIA_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// JULIA HEX LITERAL [01345689AMN-_] +/// Number format for a [`Julia`] literal floating-point number. +/// +/// [`Julia`]: https://julialang.org/ +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const JULIA_HEX_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .case_sensitive_special(true) + .integer_internal_digit_separator(true) + .fraction_internal_digit_separator(true) + .build_strict(); + +// JULIA HEX STRING [01345678MN] +/// Number format to parse a [`Julia`] float from string. +/// +/// [`Julia`]: https://julialang.org/ +#[rustfmt::skip] +#[cfg(feature = "power-of-two")] +pub const JULIA_HEX_STRING: u128 = NumberFormatBuilder::new() + .mantissa_radix(16) + .exponent_base(num::NonZeroU8::new(2)) + .exponent_radix(num::NonZeroU8::new(10)) + .build_strict(); + +/// Number format for a [`C#`] literal floating-point number. +/// +/// [`C#`]: https://learn.microsoft.com/en-us/dotnet/csharp/ +pub const CSHARP_LITERAL: u128 = CSHARP7_LITERAL; + +/// Number format to parse a [`C#`] float from string. +/// +/// [`C#`]: https://learn.microsoft.com/en-us/dotnet/csharp/ +pub const CSHARP_STRING: u128 = CSHARP7_STRING; + +// CSHARP7 LITERAL [034569ABIJKMN-_] +/// Number format for a [`C#7`] literal floating-point number. +/// +/// [`C#7`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-73 +#[rustfmt::skip] +pub const CSHARP7_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .required_fraction_digits(true) + .no_special(true) + .internal_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// CSHARP7 STRING [0134568MN] +/// Number format to parse a [`C#7`] float from string. +/// +/// [`C#7`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-73 +#[rustfmt::skip] +pub const CSHARP7_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// CSHARP6 LITERAL [03456MN] +/// Number format for a [`C#6`] literal floating-point number. +/// +/// [`C#6`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-60 +#[rustfmt::skip] +pub const CSHARP6_LITERAL: u128 = NumberFormatBuilder::new() + .required_fraction_digits(true) + .no_special(true) + .build_strict(); + +// CSHARP6 STRING [0134568MN] +/// Number format to parse a [`C#6`] float from string. +/// +/// [`C#6`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-60 +#[rustfmt::skip] +pub const CSHARP6_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// CSHARP5 LITERAL [03456MN] +/// Number format for a [`C#5`] literal floating-point number. +/// +/// [`C#5`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-50 +#[rustfmt::skip] +pub const CSHARP5_LITERAL: u128 = NumberFormatBuilder::new() + .required_fraction_digits(true) + .no_special(true) + .build_strict(); + +// CSHARP5 STRING [0134568MN] +/// Number format to parse a [`C#5`] float from string. +/// +/// [`C#5`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-50 +#[rustfmt::skip] +pub const CSHARP5_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// CSHARP4 LITERAL [03456MN] +/// Number format for a [`C#4`] literal floating-point number. +/// +/// [`C#4`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-40 +#[rustfmt::skip] +pub const CSHARP4_LITERAL: u128 = NumberFormatBuilder::new() + .required_fraction_digits(true) + .no_special(true) + .build_strict(); + +// CSHARP4 STRING [0134568MN] +/// Number format to parse a [`C#4`] float from string. +/// +/// [`C#4`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-40 +#[rustfmt::skip] +pub const CSHARP4_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// CSHARP3 LITERAL [03456MN] +/// Number format for a [`C#3`] literal floating-point number. +/// +/// [`C#3`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-30 +#[rustfmt::skip] +pub const CSHARP3_LITERAL: u128 = NumberFormatBuilder::new() + .required_fraction_digits(true) + .no_special(true) + .build_strict(); + +// CSHARP3 STRING [0134568MN] +/// Number format to parse a [`C#3`] float from string. +/// +/// [`C#3`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-30 +#[rustfmt::skip] +pub const CSHARP3_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// CSHARP2 LITERAL [03456MN] +/// Number format for a [`C#2`] literal floating-point number. +/// +/// [`C#2`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-20 +#[rustfmt::skip] +pub const CSHARP2_LITERAL: u128 = NumberFormatBuilder::new() + .required_fraction_digits(true) + .no_special(true) + .build_strict(); + +// CSHARP2 STRING [0134568MN] +/// Number format to parse a [`C#2`] float from string. +/// +/// [`C#2`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-20 +#[rustfmt::skip] +pub const CSHARP2_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// CSHARP1 LITERAL [03456MN] +/// Number format for a [`C#1`] literal floating-point number. +/// +/// [`C#1`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-12-1 +#[rustfmt::skip] +pub const CSHARP1_LITERAL: u128 = NumberFormatBuilder::new() + .required_fraction_digits(true) + .no_special(true) + .build_strict(); + +// CSHARP1 STRING [0134568MN] +/// Number format to parse a [`C#1`] float from string. +/// +/// [`C#1`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-12-1 +#[rustfmt::skip] +pub const CSHARP1_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// KAWA LITERAL [013456MN] +/// Number format for a [`Kawa`] literal floating-point number. +/// +/// [`Kawa`]: https://www.gnu.org/software/kawa/ +#[rustfmt::skip] +pub const KAWA_LITERAL: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// KAWA STRING [013456MN] +/// Number format to parse a [`Kawa`] float from string. +/// +/// [`Kawa`]: https://www.gnu.org/software/kawa/ +#[rustfmt::skip] +pub const KAWA_STRING: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// GAMBITC LITERAL [013456MN] +/// Number format for a [`Gambit-C`] literal floating-point number. +/// +/// [`Gambit-C`]: https://gambitscheme.org/ +#[rustfmt::skip] +pub const GAMBITC_LITERAL: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// GAMBITC STRING [013456MN] +/// Number format to parse a [`Gambit-C`] float from string. +/// +/// [`Gambit-C`]: https://gambitscheme.org/ +#[rustfmt::skip] +pub const GAMBITC_STRING: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// GUILE LITERAL [013456MN] +/// Number format for a [`Guile`] literal floating-point number. +/// +/// [`Guile`]: https://www.gnu.org/software/guile/ +#[rustfmt::skip] +pub const GUILE_LITERAL: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// GUILE STRING [013456MN] +/// Number format to parse a [`Guile`] float from string. +/// +/// [`Guile`]: https://www.gnu.org/software/guile/ +#[rustfmt::skip] +pub const GUILE_STRING: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// CLOJURE LITERAL [13456MN] +/// Number format for a [`Clojure`] literal floating-point number. +/// +/// [`Clojure`]: https://clojure.org/ +#[rustfmt::skip] +pub const CLOJURE_LITERAL: u128 = NumberFormatBuilder::new() + .required_integer_digits(true) + .no_special(true) + .build_strict(); + +// CLOJURE STRING [01345678MN] +/// Number format to parse a [`Clojure`] float from string. +/// +/// [`Clojure`]: https://clojure.org/ +#[rustfmt::skip] +pub const CLOJURE_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// ERLANG LITERAL [34578MN] +/// Number format for an [`Erlang`] literal floating-point number. +/// +/// [`Erlang`]: https://www.erlang.org/ +#[rustfmt::skip] +pub const ERLANG_LITERAL: u128 = NumberFormatBuilder::new() + .required_digits(true) + .no_exponent_without_fraction(true) + .case_sensitive_special(true) + .build_strict(); + +// ERLANG STRING [345MN] +/// Number format to parse an [`Erlang`] float from string. +/// +/// [`Erlang`]: https://www.erlang.org/ +#[rustfmt::skip] +pub const ERLANG_STRING: u128 = NumberFormatBuilder::new() + .required_digits(true) + .no_exponent_without_fraction(true) + .no_special(true) + .build_strict(); + +// ELM LITERAL [456] +/// Number format for an [`Elm`] literal floating-point number. +/// +/// [`Elm`]: https://elm-lang.org/ +#[rustfmt::skip] +pub const ELM_LITERAL: u128 = NumberFormatBuilder::new() + .required_digits(true) + .no_positive_mantissa_sign(true) + .no_integer_leading_zeros(true) + .no_float_leading_zeros(true) + .no_special(true) + .build_strict(); + +// ELM STRING [01345678MN] +// Note: There is no valid representation of NaN, just Infinity. +/// Number format to parse an [`Elm`] float from string. +/// +/// [`Elm`]: https://elm-lang.org/ +#[rustfmt::skip] +pub const ELM_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// SCALA LITERAL [3456] +/// Number format for a [`Scala`] literal floating-point number. +/// +/// [`Scala`]: https://www.scala-lang.org/ +#[rustfmt::skip] +pub const SCALA_LITERAL: u128 = NumberFormatBuilder::new() + .required_digits(true) + .no_special(true) + .no_integer_leading_zeros(true) + .no_float_leading_zeros(true) + .build_strict(); + +// SCALA STRING [01345678MN] +/// Number format to parse a [`Scala`] float from string. +/// +/// [`Scala`]: https://www.scala-lang.org/ +#[rustfmt::skip] +pub const SCALA_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// ELIXIR LITERAL [3459ABMN-_] +/// Number format for an [`Elixir`] literal floating-point number. +/// +/// [`Elixir`]: https://elixir-lang.org/ +#[rustfmt::skip] +pub const ELIXIR_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .required_digits(true) + .no_exponent_without_fraction(true) + .no_special(true) + .internal_digit_separator(true) + .build_strict(); + +// ELIXIR STRING [345MN] +/// Number format to parse an [`Elixir`] float from string. +/// +/// [`Elixir`]: https://elixir-lang.org/ +#[rustfmt::skip] +pub const ELIXIR_STRING: u128 = NumberFormatBuilder::new() + .required_digits(true) + .no_exponent_without_fraction(true) + .no_special(true) + .build_strict(); + +// FORTRAN LITERAL [013456MN] +/// Number format for a [`FORTRAN`] literal floating-point number. +/// +/// [`FORTRAN`]: https://fortran-lang.org/ +#[rustfmt::skip] +pub const FORTRAN_LITERAL: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// FORTRAN STRING [0134567MN] +/// Number format to parse a [`FORTRAN`] float from string. +/// +/// [`FORTRAN`]: https://fortran-lang.org/ +#[rustfmt::skip] +pub const FORTRAN_STRING: u128 = NumberFormatBuilder::new().build_strict(); + +// D LITERAL [0134569ABFGHIJKN-_] +/// Number format for a [`D`] literal floating-point number. +/// +/// [`D`]: https://dlang.org/ +#[rustfmt::skip] +pub const D_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .no_special(true) + .no_integer_leading_zeros(true) + .internal_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// D STRING [01345679AFGMN-_] +/// Number format to parse a [`D`] float from string. +/// +/// [`D`]: https://dlang.org/ +#[rustfmt::skip] +pub const D_STRING: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_internal_digit_separator(true) + .fraction_internal_digit_separator(true) + .integer_trailing_digit_separator(true) + .fraction_trailing_digit_separator(true) + .build_strict(); + +// COFFEESCRIPT LITERAL [01345678] +/// Number format for a [`Coffeescript`] literal floating-point number. +/// +/// [`Coffeescript`]: https://coffeescript.org/ +#[rustfmt::skip] +pub const COFFEESCRIPT_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .no_integer_leading_zeros(true) + .no_float_leading_zeros(true) + .build_strict(); + +// COFFEESCRIPT STRING [012345678MN] +/// Number format to parse a [`Coffeescript`] float from string. +/// +/// [`Coffeescript`]: https://coffeescript.org/ +#[rustfmt::skip] +pub const COFFEESCRIPT_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// COBOL LITERAL [0345MN] +/// Number format for a [`Cobol`] literal floating-point number. +/// +/// [`Cobol`]: https://www.ibm.com/think/topics/cobol +#[rustfmt::skip] +pub const COBOL_LITERAL: u128 = NumberFormatBuilder::new() + .required_fraction_digits(true) + .no_exponent_without_fraction(true) + .no_special(true) + .build_strict(); + +// COBOL STRING [012356MN] +/// Number format to parse a [`Cobol`] float from string. +/// +/// [`Cobol`]: https://www.ibm.com/think/topics/cobol +#[rustfmt::skip] +pub const COBOL_STRING: u128 = NumberFormatBuilder::new() + .required_exponent_sign(true) + .no_special(true) + .build_strict(); + +// FSHARP LITERAL [13456789ABIJKMN-_] +/// Number format for a [`F#`] literal floating-point number. +/// +/// [`F#`]: https://fsharp.org/ +#[rustfmt::skip] +pub const FSHARP_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .required_integer_digits(true) + .required_exponent_digits(true) + .case_sensitive_special(true) + .internal_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// FSHARP STRING [013456789ABCDEFGHIJKLMN-_] +/// Number format to parse a [`F#`] float from string. +/// +/// [`F#`]: https://fsharp.org/ +#[rustfmt::skip] +pub const FSHARP_STRING: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .internal_digit_separator(true) + .leading_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .special_digit_separator(true) + .build_strict(); + +// VB LITERAL [03456MN] +/// Number format for a [`Visual Basic`] literal floating-point number. +/// +/// [`Visual Basic`]: https://learn.microsoft.com/en-us/dotnet/visual-basic/ +#[rustfmt::skip] +pub const VB_LITERAL: u128 = NumberFormatBuilder::new() + .required_fraction_digits(true) + .no_special(true) + .build_strict(); + +// VB STRING [01345678MN] +/// Number format to parse a [`Visual Basic`] float from string. +/// +/// [`Visual Basic`]: https://learn.microsoft.com/en-us/dotnet/visual-basic/ +// Note: To my knowledge, Visual Basic cannot parse infinity. +#[rustfmt::skip] +pub const VB_STRING: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// OCAML LITERAL [1456789ABDFGHIJKMN-_] +/// Number format for an [`OCaml`] literal floating-point number. +/// +/// [`OCaml`]: https://ocaml.org/ +#[rustfmt::skip] +pub const OCAML_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .required_integer_digits(true) + .required_exponent_digits(true) + .no_positive_mantissa_sign(true) + .case_sensitive_special(true) + .internal_digit_separator(true) + .fraction_leading_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// OCAML STRING [01345679ABCDEFGHIJKLMN-_] +/// Number format to parse an [`OCaml`] float from string. +/// +/// [`OCaml`]: https://ocaml.org/ +#[rustfmt::skip] +pub const OCAML_STRING: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .internal_digit_separator(true) + .leading_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .special_digit_separator(true) + .build_strict(); + +// OBJECTIVEC LITERAL [013456MN] +/// Number format for an [`Objective-C`] literal floating-point number. +/// +/// [`Objective-C`]: https://en.wikipedia.org/wiki/Objective-C +#[rustfmt::skip] +pub const OBJECTIVEC_LITERAL: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// OBJECTIVEC STRING [013456MN] +/// Number format to parse an [`Objective-C`] float from string. +/// +/// [`Objective-C`]: https://en.wikipedia.org/wiki/Objective-C +#[rustfmt::skip] +pub const OBJECTIVEC_STRING: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// REASONML LITERAL [13456789ABDFGHIJKMN-_] +/// Number format for a [`ReasonML`] literal floating-point number. +/// +/// [`ReasonML`]: https://reasonml.github.io/ +#[rustfmt::skip] +pub const REASONML_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .required_integer_digits(true) + .required_exponent_digits(true) + .case_sensitive_special(true) + .internal_digit_separator(true) + .fraction_leading_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// REASONML STRING [01345679ABCDEFGHIJKLMN-_] +/// Number format to parse a [`ReasonML`] float from string. +/// +/// [`ReasonML`]: https://reasonml.github.io/ +#[rustfmt::skip] +pub const REASONML_STRING: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .internal_digit_separator(true) + .leading_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .special_digit_separator(true) + .build_strict(); + +// OCTAVE LITERAL [013456789ABDFGHIJKMN-_] +// Note: Octave accepts both NaN and nan, Inf and inf. +/// Number format for an [`Octave`] literal floating-point number. +/// +/// [`Octave`]: https://octave.org/ +#[rustfmt::skip] +pub const OCTAVE_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .case_sensitive_special(true) + .internal_digit_separator(true) + .fraction_leading_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// OCTAVE STRING [01345679ABCDEFGHIJKMN-,] +/// Number format to parse an [`Octave`] float from string. +/// +/// [`Octave`]: https://octave.org/ +#[rustfmt::skip] +pub const OCTAVE_STRING: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b',')) + .internal_digit_separator(true) + .leading_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// MATLAB LITERAL [013456789ABDFGHIJKMN-_] +// Note: Matlab accepts both NaN and nan, Inf and inf. +/// Number format for an [`Matlab`] literal floating-point number. +/// +/// [`Matlab`]: https://www.mathworks.com/products/matlab.html +#[rustfmt::skip] +pub const MATLAB_LITERAL: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .case_sensitive_special(true) + .internal_digit_separator(true) + .fraction_leading_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// MATLAB STRING [01345679ABCDEFGHIJKMN-,] +/// Number format to parse an [`Matlab`] float from string. +/// +/// [`Matlab`]: https://www.mathworks.com/products/matlab.html +#[rustfmt::skip] +pub const MATLAB_STRING: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b',')) + .internal_digit_separator(true) + .leading_digit_separator(true) + .trailing_digit_separator(true) + .consecutive_digit_separator(true) + .build_strict(); + +// ZIG LITERAL [1456MN] +/// Number format for a [`Zig`] literal floating-point number. +/// +/// [`Zig`]: https://ziglang.org/ +#[rustfmt::skip] +pub const ZIG_LITERAL: u128 = NumberFormatBuilder::new() + .required_integer_digits(true) + .no_positive_mantissa_sign(true) + .no_special(true) + .build_strict(); + +// ZIG STRING [01234567MN] +/// Number format to parse a [`Zig`] float from string. +/// +/// [`Zig`]: https://ziglang.org/ +pub const ZIG_STRING: u128 = PERMISSIVE; + +// SAGE LITERAL [012345678MN] +// Note: Both Infinity and infinity are accepted. +/// Number format for a [`Sage`] literal floating-point number. +/// +/// [`Sage`]: https://www.sagemath.org/ +#[rustfmt::skip] +pub const SAGE_LITERAL: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .build_strict(); + +// SAGE STRING [01345679ABMN-_] +/// Number format to parse a [`Sage`] float from string. +/// +/// [`Sage`]: https://www.sagemath.org/ +#[rustfmt::skip] +pub const SAGE_STRING: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .internal_digit_separator(true) + .build_strict(); + +// JSON [456] +/// Number format for a [`JSON`][`JSON-REF`] literal floating-point number. +/// +/// [`JSON-REF`]: https://www.json.org/json-en.html +#[rustfmt::skip] +pub const JSON: u128 = NumberFormatBuilder::new() + .required_digits(true) + .no_positive_mantissa_sign(true) + .no_special(true) + .no_integer_leading_zeros(true) + .no_float_leading_zeros(true) + .build_strict(); + +// TOML [34569AB] +/// Number format for a [`TOML`][`TOML-REF`] literal floating-point number. +/// +/// [`TOML-REF`]: https://toml.io/en/ +#[rustfmt::skip] +pub const TOML: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .required_digits(false) + .no_special(true) + .no_integer_leading_zeros(true) + .no_float_leading_zeros(true) + .internal_digit_separator(true) + .build_strict(); + +// YAML (defined in-terms of JSON schema). +/// Number format for a [`YAML`][`YAML-REF`] literal floating-point number. +/// +/// [`YAML-REF`]: https://yaml.org/ +pub const YAML: u128 = JSON; + +// XML [01234578MN] +/// Number format for an [`XML`][`XML-REF`] literal floating-point number. +/// +/// [`XML-REF`]: https://en.wikipedia.org/wiki/XML +#[rustfmt::skip] +pub const XML: u128 = NumberFormatBuilder::new() + .required_exponent_digits(false) + .case_sensitive_special(true) + .build_strict(); + +// SQLITE [013456MN] +/// Number format for a [`SQLite`] literal floating-point number. +/// +/// [`SQLite`]: https://www.sqlite.org/ +#[rustfmt::skip] +pub const SQLITE: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// POSTGRESQL [013456MN] +/// Number format for a [`PostgreSQL`] literal floating-point number. +/// +/// [`PostgreSQL`]: https://www.postgresql.org/ +#[rustfmt::skip] +pub const POSTGRESQL: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// MYSQL [013456MN] +/// Number format for a [`MySQL`] literal floating-point number. +/// +/// [`MySQL`]: https://www.mysql.com/ +#[rustfmt::skip] +pub const MYSQL: u128 = NumberFormatBuilder::new() + .no_special(true) + .build_strict(); + +// MONGODB [01345678M] +/// Number format for a [`MongoDB`] literal floating-point number. +/// +/// [`MongoDB`]: https://www.mongodb.com/ +#[rustfmt::skip] +pub const MONGODB: u128 = NumberFormatBuilder::new() + .case_sensitive_special(true) + .no_float_leading_zeros(true) + .build_strict(); + +// HIDDEN DEFAULTS AND INTERFACES + +/// Number format when no flags are set. +#[doc(hidden)] +#[rustfmt::skip] +pub const PERMISSIVE: u128 = NumberFormatBuilder::new() + .required_exponent_digits(false) + .required_mantissa_digits(false) + .build_strict(); + +/// Number format when all digit separator flags are set. +#[doc(hidden)] +#[rustfmt::skip] +pub const IGNORE: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .digit_separator_flags(true) + .required_exponent_digits(false) + .required_mantissa_digits(false) + .build_strict(); diff --git a/rust/vendor/lexical-util/src/result.rs b/rust/vendor/lexical-util/src/result.rs new file mode 100644 index 0000000..44fbe28 --- /dev/null +++ b/rust/vendor/lexical-util/src/result.rs @@ -0,0 +1,8 @@ +//! Result type for numeric parsing functions. + +use core::result; + +use crate::error; + +/// A specialized [`Result`][`result::Result`] type for lexical operations. +pub type Result = result::Result; diff --git a/rust/vendor/lexical-util/src/skip.rs b/rust/vendor/lexical-util/src/skip.rs new file mode 100644 index 0000000..66a3ff2 --- /dev/null +++ b/rust/vendor/lexical-util/src/skip.rs @@ -0,0 +1,1584 @@ +//! An iterator that skips values equal to a provided value. +//! +//! Iterators over a contiguous slice, returning all values +//! except for those matching the provided skip value. +//! +//! # Complexity +//! +//! Although superficially quite simple, the level of complexity +//! introduced by digit separators can be quite complex, due +//! the number of permutations during parsing. +//! +//! We can consume any combinations of of \[0,3\] items from the following set: +//! - \[l\]eading digit separators, where digit separators occur before +//! digits. +//! - \[i\]nternal digit separators, where digit separators occur between +//! digits. +//! - \[t\]railing digit separators, where digit separators occur after +//! digits. +//! +//! In addition to those combinations, we can also have: +//! - \[c\]onsecutive digit separators, which allows two digit separators to +//! be adjacent. +//! +//! # Shorthand +//! +//! We will use the term consumer to denote a function that consumes digits, +//! splitting an input buffer at an index, where the leading section contains +//! valid input digits, and the trailing section contains invalid characters. +//! Due to the number of combinations for consumers, we use the following +//! shorthand to denote consumers: +//! - `no`, does not use a digit separator. +//! - `l`, consumes leading digit separators. +//! - `i`, consumes internal digit separators. +//! - `t`, consumes trailing digit separators. +//! - `c`, consumes consecutive digit separators. +//! +//! The `next`/`iter` algorithms are therefore named `next_x`, where `x` +//! represents the shorthand name of the consumer, in sorted order. +//! For example, `next_ilt` means that consumer can skip internal, +//! leading, and trailing digit separators, but not consecutive ones. + +#![cfg(all(feature = "format", any(feature = "parse-floats", feature = "parse-integers")))] + +use core::{mem, ptr}; + +use crate::digit::char_is_digit_const; +use crate::format::NumberFormat; +use crate::format_flags as flags; +use crate::iterator::{DigitsIter, Iter}; + +// IS_ILTC +// ------- + +// NOTE: The compiler optimizes all these methods pretty well: it's as +// efficient or almost as efficient as optimized assembly without unsafe +// code, especially since we have to do bounds checking +// before and the compiler can determine all cases correctly. + +/// Helpers to get the next or previous elements for checks. +/// +/// This has the non-consecutive iterator variants as well +/// as the consecutive ones. The consecutive ones will iteratively +/// process all digits. +macro_rules! indexing { + (@next $self:ident, $index:expr) => { + $index.wrapping_add(1) + }; + + (@nextc $self:ident, $index:expr) => {{ + let mut index = $index; + let slc = $self.byte.slc; + while slc.get(index.wrapping_add(1)).map_or(false, |&x| $self.is_digit_separator(x)) { + index = index.wrapping_add(1); + } + index.wrapping_add(1) + }}; + + (@prev $self:ident, $index:expr) => { + $index.wrapping_sub(1) + }; + + (@prevc $self:ident, $index:expr) => {{ + let mut index = $index; + let slc = $self.byte.slc; + while slc.get(index.wrapping_sub(1)).map_or(false, |&x| $self.is_digit_separator(x)) { + index = index.wrapping_sub(1); + } + index.wrapping_sub(1) + }}; +} + +/// Determine if a single digit separator is internal. +/// +/// # Examples +/// +/// - `1__1_23`- invalid +/// - `1_1__23`- invalid +/// - `1_1_23`- valid +/// - `11_x23`- invalid +/// - `_1123`- invalid +/// - `+_1123`- invalid +/// - `_+1123`- invalid +/// - `1123_`- invalid +/// - `1123_.`- invalid +/// - `112_3.`- valid +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_i { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index - 1` is a digit + // - `index + 1` is a digit + + let prev = indexing!(@prev $self, $index); + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + slc.get(prev).map_or(false, |&x| $self.is_digit(x)) && + slc.get(next).map_or(false, |&x| $self.is_digit(x)) + }}; + + (@first $self:ident) => { + is_i!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => {{ + // NOTE: We must have validated `prev`, so this just checks `next`. + // NOTE: The conditions here then are that: + // - `index + 1` is a digit + + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(false, |&x| $self.is_digit(x)) + }}; + + (@internal $self:ident) => { + is_i!(@internal $self, $self.byte.index) + }; +} + +/// Determine if consecutive digit separators are internal. +/// +/// # Examples +/// +/// - `1__1_23`- valid +/// - `1_1__23`- valid +/// - `1_1_23`- valid +/// - `11_x23`- invalid +/// - `_1123`- invalid +/// - `+_1123`- invalid +/// - `_+1123`- invalid +/// - `1123_`- invalid +/// - `1123_.`- invalid +/// - `112_3.`- valid +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_ic { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index - 1` is a digit after consuming digit separators + // - `index + 1` is a digit after consuming digit separators + + let prev = indexing!(@prevc $self, $index); + let next = indexing!(@nextc $self, $index); + let slc = $self.byte.slc; + slc.get(prev).map_or(false, |&x| $self.is_digit(x)) && + slc.get(next).map_or(false, |&x| $self.is_digit(x)) + }}; + + (@first $self:ident) => { + is_ic!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => {{ + // NOTE: We must have validated `prev`, so this just checks `next`. + // NOTE: The conditions here then are that: + // - `index + 1` is a digit after consuming digit separators + + let next = indexing!(@nextc $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(false, |&x| $self.is_digit(x)) + }}; + + (@internal $self:ident) => { + is_ic!(@internal $self, $self.byte.index) + }; +} + +/// Determine if a single digit separator is leading. +/// +/// # Examples +/// +/// - `__123`- invalid +/// - `+__123`- invalid +/// - `._123`- valid +/// - `_+123`- valid +/// - `_123`- valid +/// - `+_123`- valid +/// +/// Having a subsequent sign character is fine since it might +/// be part of a partial parser. +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_l { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index - 1` is not a digit + // - `index - 1` is not a digit separator + // - `index + 1` is not a digit separator + + let prev = indexing!(@prev $self, $index); + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + slc.get(prev).map_or(true, |&x| !$self.is_digit(x) && !$self.is_digit_separator(x)) && + slc.get(next).map_or(true, |&x| !$self.is_digit_separator(x)) + }}; + + (@first $self:ident) => { + is_l!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => {{ + // NOTE: Previous must have been a digit so this cannot be valid. + false + }}; + + (@internal $self:ident) => { + is_l!(@internal $self, $self.byte.index) + }; +} + +/// Determine if one or more digit separators are leading. +/// +/// # Examples +/// +/// - `__123`- valid +/// - `+__123`- valid +/// - `+__+123`- valid +/// - `+__.123`- valid +/// - `._123`- valid +/// - `_+123`- invalid +/// - `_123`- valid +/// - `+_123`- valid +/// +/// Having a subsequent sign character is fine since it might +/// be part of a partial parser. +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_lc { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index - 1` is not a digit after removing digit separators + + let prev = indexing!(@prevc $self, $index); + let slc = $self.byte.slc; + slc.get(prev).map_or(true, |&x| !$self.is_digit(x)) + }}; + + (@first $self:ident) => { + is_lc!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => {{ + // NOTE: Previous must have been a digit so this cannot be valid. + false + }}; + + (@internal $self:ident) => { + is_lc!(@internal $self, $self.byte.index) + }; +} + +/// Determine if a single digit separator is trailing. +/// +/// # Examples +/// +/// - `123_`- valid +/// - `123__`- invalid +/// - `123_.`- valid +/// - `123__.`- invalid +/// - `123_1`- invalid +/// - `123__1`- invalid +/// - _: valid +/// - _+: valid +/// - 1_+: valid +/// +/// Having a subsequent sign character is fine since it might +/// be part of a partial parser. +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_t { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index + 1` is not a digit + // - `index + 1` is not a digit separator + // - `index - 1` is not a digit separator + + let prev = indexing!(@prev $self, $index); + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(true, |&x| !$self.is_digit(x) && !$self.is_digit_separator(x)) && + slc.get(prev).map_or(true, |&x| !$self.is_digit_separator(x)) + }}; + + (@first $self:ident) => { + is_t!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => {{ + // NOTE: We must have validated `prev`, so this just checks `next`. + // NOTE: The conditions here then are that: + // - `index + 1` is not a digit + // - `index + 1` is not a digit separator + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(true, |&x| !$self.is_digit(x) && !$self.is_digit_separator(x)) + }}; + + (@internal $self:ident) => { + is_t!(@internal $self, $self.byte.index) + }; +} + +/// Determine if one or more digit separators are trailing. +/// +/// # Examples +/// +/// - `123_`- valid +/// - `123__`- valid +/// - `123_.`- valid +/// - `123__.`- valid +/// - `123_1`- invalid +/// - `123__1`- invalid +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_tc { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index + 1` is not a digit + + let next = indexing!(@nextc $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(true, |&x| !$self.is_digit(x)) + }}; + + (@first $self:ident) => { + is_tc!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => { + // NOTE: This is already optimized for the first case. + is_tc!(@first $self, $index) + }; + + (@internal $self:ident) => { + is_tc!(@internal $self, $self.byte.index) + }; +} + +/// Determine if the digit separator is leading or internal. +/// +/// # Examples +/// +/// - `__123`- invalid +/// - `+__123`- invalid +/// - `._123`- valid +/// - `_+123`- valid +/// - `_123`- valid +/// - `+_123`- valid +/// - `+1_23`- valid +/// - `+1__23`- invalid +/// - `+123_`- invalid +/// - `+123__`- invalid +/// - _: valid +/// - _+: valid +/// - 1_+: invalid +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_il { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index + 1` is a digit + // - `index + 1` is not a digit separator + // - `index - 1` is not a digit separator + // + // # Logic + // + // If the previous character is a digit, then the + // next character must be a digit. If the previous + // character is not a digit, then the subsequent character can + // be anything besides a digit separator + + let prev = indexing!(@prev $self, $index); + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + + if slc.get(prev).map_or(false, |&x| $self.is_digit(x)) { + slc.get(next).map_or(false, |&x| $self.is_digit(x)) + } else { + slc.get(prev).map_or(true, |&x| !$self.is_digit_separator(x)) + } + }}; + + (@first $self:ident) => { + is_il!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => {{ + // NOTE: We must have validated `prev`, so this just checks `next`. + // NOTE: The conditions here then are that: + // - `index + 1` is a digit + + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(false, |&x| $self.is_digit(x)) + }}; + + (@internal $self:ident) => { + is_il!(@internal $self, $self.byte.index) + }; +} + +/// Determine if consecutive digit separators are leading or internal. +/// +/// # Examples +/// +/// - `__123`- valid +/// - `+__123`- valid +/// - `._123`- valid +/// - `_+123`- valid +/// - `_123`- valid +/// - `+_123`- valid +/// - `+1_23`- valid +/// - `+1__23`- valid +/// - `+123_`- invalid +/// - `+123__`- invalid +/// - _: valid +/// - _+: valid +/// - 1_+: invalid +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_ilc { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index + 1` is a digit after consuming digit separators + // + // # Logic + // + // We also need to consider the case where it's empty, + // that is, the previous one wasn't a digit if we don't + // have a digit. + + let prev = indexing!(@prevc $self, $index); + let next = indexing!(@nextc $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(false, |&x| $self.is_digit(x)) || + slc.get(prev).map_or(true, |&x| !$self.is_digit(x)) + }}; + + (@first $self:ident) => { + is_ilc!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index + 1` is a digit after consuming digit separators + + let next = indexing!(@nextc $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(true, |&x| $self.is_digit(x)) + }}; + + (@internal $self:ident) => { + is_ilc!(@internal $self, $self.byte.index) + }; +} + +/// Determine if the digit separator is internal or trailing. +/// +/// # Examples +/// +/// - `__123`- valid +/// - `+__123`- valid +/// - `._123`- valid +/// - `_+123`- valid +/// - `_123`- valid +/// - `+_123`- valid +/// - `+1_23`- valid +/// - `+1__23`- valid +/// - `+123_`- invalid +/// - `+123__`- invalid +/// - _: valid +/// - _+: valid +/// - 1_+: invalid +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_it { + (@first$self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index - 1` is a digit + // - `index - 1` is not a digit separator + // - `index + 1` is not a digit separator + // + // # Logic + // + // If the previous character is not a digit, there cannot + // be a digit for a following character. If the previous + // character is a digit, then the following one must be + // a digit as well. + + let prev = indexing!(@prev $self, $index); + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + if slc.get(prev).map_or(false, |&x| $self.is_digit(x)) { + // Have a digit, any character besides a digit separator is valid + slc.get(next).map_or(true, |&x| !$self.is_digit_separator(x)) + } else { + // Not a digit, so we cannot have a digit or a digit separator + slc.get(next).map_or(true, |&x| !$self.is_digit(x) && !$self.is_digit_separator(x)) + } + }}; + + (@first$self:ident) => { + is_it!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => {{ + // NOTE: We must have validated `prev`, so this just checks `next`. + // NOTE: The conditions here then are that: + // - `index + 1` is not a digit separator + // Since we've previously had a digit, this is guaranteed to + // be internal or trailing. + + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(true, |&x| !$self.is_digit_separator(x)) + }}; + + (@internal $self:ident) => { + is_it!(@internal $self, $self.byte.index) + }; +} + +/// Determine if consecutive digit separators are internal or trailing. +/// +/// # Examples +/// +/// - `__123`- invalid +/// - `+__123`- invalid +/// - `._123`- invalid +/// - `_+123`- invalid +/// - `_123`- invalid +/// - `+_123`- invalid +/// - `+1_23`- valid +/// - `+1__23`- valid +/// - `+123_`- valid +/// - `+123__`- valid +/// - _: valid +/// - _+: valid +/// - 1_+: valid +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_itc { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index - 1` is not a digit after consuming digit separators + // + // # Logic + // + // We also need to consider the case where it's empty, + // that is, the previous one wasn't a digit if we don't + // have a digit. + + let prev = indexing!(@prevc $self, $index); + let next = indexing!(@nextc $self, $index); + let slc = $self.byte.slc; + slc.get(prev).map_or(false, |&x| !$self.is_digit(x)) || + slc.get(next).map_or(true, |&x| !$self.is_digit(x)) + }}; + + (@first $self:ident) => { + is_itc!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => { + // NOTE: Previous must have been a digit so this must be valid. + true + }; + + (@internal $self:ident) => { + is_itc!(@internal $self, $self.byte.index) + }; +} + +/// Determine if the digit separator is leading or trailing. +/// +/// # Examples +/// +/// - `__123`- invalid +/// - `+__123`- invalid +/// - `._123`- valid +/// - `_+123`- valid +/// - `_123`- valid +/// - `+_123`- valid +/// - `+1_23`- invalid +/// - `+1__23`- invalid +/// - `+123_`- valid +/// - `+123__`- invalid +/// - _: valid +/// - _+: valid +/// - 1_+: valid +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_lt { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - not (`index - 1` is a digit and `index + 1` is a digit) + // - `index - 1` is not a digit separator + // - `index + 1` is not a digit separator + + let prev = indexing!(@prev $self, $index); + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + let prev_value = slc.get(prev); + let next_value = slc.get(next); + + let is_prev_sep = prev_value.map_or(false, |&x| $self.is_digit_separator(x)); + let is_prev_dig = prev_value.map_or(false, |&x| $self.is_digit(x)); + let is_next_sep = next_value.map_or(false, |&x| $self.is_digit_separator(x)); + let is_next_dig = next_value.map_or(false, |&x| $self.is_digit(x)); + + !is_prev_sep && !is_next_sep && !(is_prev_dig && is_next_dig) + }}; + + (@first $self:ident) => { + is_lt!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => {{ + // NOTE: We must have validated `prev`, so this just checks `next`. + // NOTE: The conditions here then are that: + // - `index + 1` is not a digit + // - `index + 1` is not a digit separator + + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(true, |&x| !$self.is_digit(x) && !$self.is_digit_separator(x)) + }}; + + (@internal $self:ident) => { + is_lt!(@internal $self, $self.byte.index) + }; +} + +/// Determine if consecutive digit separators are leading or trailing. +/// +/// # Examples +/// +/// - `__123`- valid +/// - `+__123`- valid +/// - `._123`- valid +/// - `_+123`- valid +/// - `_123`- valid +/// - `+_123`- valid +/// - `+1_23`- invalid +/// - `+1__23`- invalid +/// - `+123_`- valid +/// - `+123__`- valid +/// - _: valid +/// - _+: valid +/// - 1_+: valid +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_ltc { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that (after consuming separators): + // - not (`index - 1` is a digit and `index + 1` is a digit) + + let prev = indexing!(@prevc $self, $index); + let next = indexing!(@nextc $self, $index); + let slc = $self.byte.slc; + !(slc.get(prev).map_or(false, |&x| $self.is_digit(x)) && slc.get(next).map_or(false, |&x| $self.is_digit(x))) + }}; + + (@first $self:ident) => { + is_ltc!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => {{ + // NOTE: We must have validated `prev`, so this just checks `next`. + // NOTE: The conditions here then are that: + // - `index + 1` is not a digit + + let next = indexing!(@nextc $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(true, |&x| !$self.is_digit(x)) + }}; + + (@internal $self:ident) => { + is_ltc!(@internal $self, $self.byte.index) + }; +} + +/// Determine if a single digit separator is internal, leading, or trailing. +/// +/// # Examples +/// +/// - `__123`- invalid +/// - `+__123`- invalid +/// - `._123`- valid +/// - `_+123`- valid +/// - `_123`- valid +/// - `+_123`- valid +/// - `+1_23`- valid +/// - `+1__23`- invalid +/// - `+123_`- valid +/// - `+123__`- invalid +/// - _: valid +/// - _+: valid +/// - 1_+: valid +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_ilt { + (@first $self:ident, $index:expr) => {{ + // NOTE: The conditions here then are that: + // - `index + 1` is not a digit separator + // - `index - 1` is not a digit separator + + let prev = indexing!(@prev $self, $index); + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + !slc.get(next).map_or(false, |&x| $self.is_digit_separator(x)) && + !slc.get(prev).map_or(false, |&x| $self.is_digit_separator(x)) + }}; + + (@first $self:ident) => { + is_ilt!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => {{ + // NOTE: We must have validated `prev`, so this just checks `next`. + // NOTE: The conditions here then are that: + // - `index + 1` is not a digit separator + + let next = indexing!(@next $self, $index); + let slc = $self.byte.slc; + slc.get(next).map_or(true, |&x| !$self.is_digit_separator(x)) + }}; + + (@internal $self:ident) => { + is_ilt!(@internal $self, $self.byte.index) + }; +} + +/// Determine if consecutive digit separators are internal, leading, or +/// trailing. +/// +/// This is always true. +/// +/// # Examples +/// +/// - `__123`- valid +/// - `+__123`- valid +/// - `._123`- valid +/// - `_+123`- valid +/// - `_123`- valid +/// - `+_123`- valid +/// - `+1_23`- valid +/// - `+1__23`- valid +/// - `+123_`- valid +/// - `+123__`- valid +/// - _: valid +/// - _+: valid +/// - 1_+: valid +/// +/// # Preconditions +/// +/// Assumes `slc[index]` is a digit separator. +macro_rules! is_iltc { + (@first $self:ident, $index:expr) => { + true + }; + + (@first $self:ident) => { + is_iltc!(@first $self, $self.byte.index) + }; + + (@internal $self:ident, $index:expr) => { + true + }; + + (@internal $self:ident) => { + is_iltc!(@internal $self, $self.byte.index) + }; +} + +// PEEK +// ---- + +/// Consumes 1 or more digit separators. +/// Peeks the next token that's not a digit separator. +macro_rules! peek_1 { + ($self:ident, $is_skip:ident) => {{ + // This will consume a single, non-consecutive digit separators. + let index = $self.cursor(); + let buffer = $self.get_buffer(); + let value = buffer.get(index)?; + let is_digit_separator = $self.is_digit_separator(*value); + // NOTE: We can do some pretty major optimizations for internal values, + // since we can check the location and don't need to check previous values. + if is_digit_separator { + // NOTE: This cannot iteratively search for the next value, + // or else the consecutive digit separator has no effect (#96). + let is_skip = if $self.current_count() == 0 { + $is_skip!(@first $self) + } else { + $is_skip!(@internal $self) + }; + if is_skip { + // SAFETY: Safe since `index < buffer.len()`, so `index + 1 <= buffer.len()`` + unsafe { $self.set_cursor(index + 1) }; + buffer.get(index + 1) + } else { + Some(value) + } + } else { + // Have 1 of 2 conditions: + // 1. A non-digit separator character. + // 2. A digit separator that is not valid in the context. + Some(value) + } + }}; +} + +/// Consumes 1 or more digit separators. +/// Peeks the next token that's not a digit separator. +macro_rules! peek_n { + ($self:ident, $is_skip:ident) => {{ + // This will consume consecutive digit separators. + let mut index = $self.cursor(); + let buffer = $self.get_buffer(); + let value = buffer.get(index)?; + let is_digit_separator = $self.is_digit_separator(*value); + // NOTE: We can do some pretty major optimizations for internal values, + // since we can check the location and don't need to check previous values. + if is_digit_separator { + let is_skip = if $self.current_count() == 0 { + $is_skip!(@first $self) + } else { + $is_skip!(@internal $self) + }; + if is_skip { + // Have a skippable digit separator: keep incrementing until we find + // a non-digit separator character. Don't need any complex checks + // here, since we've already done them above. + index += 1; + while index < buffer.len() + && buffer.get(index).map_or(false, |&x| $self.is_digit_separator(x)) + { + index += 1; + } + // SAFETY: Safe since `index <= buffer.len()`. + unsafe { $self.set_cursor(index) }; + buffer.get(index) + } else { + Some(value) + } + } else { + // Have 1 of 2 conditions: + // 1. A non-digit separator character. + // 2. A digit separator that is not valid in the context. + Some(value) + } + }}; +} + +/// Consumes no digit separators and peeks the next value. +macro_rules! peek_noskip { + ($self:ident) => { + $self.byte.slc.get($self.byte.index) + }; +} + +/// Consumes at most 1 leading digit separator and peeks the next value. +macro_rules! peek_l { + ($self:ident) => { + peek_1!($self, is_l) + }; +} + +/// Consumes at most 1 internal digit separator and peeks the next value. +macro_rules! peek_i { + ($self:ident) => { + peek_1!($self, is_i) + }; +} + +/// Consumes at most 1 trailing digit separator and peeks the next value. +macro_rules! peek_t { + ($self:ident) => { + peek_1!($self, is_t) + }; +} + +/// Consumes at most 1 internal/leading digit separator and peeks the next +/// value. +macro_rules! peek_il { + ($self:ident) => { + peek_1!($self, is_il) + }; +} + +/// Consumes at most 1 internal/trailing digit separator and peeks the next +/// value. +macro_rules! peek_it { + ($self:ident) => { + peek_1!($self, is_it) + }; +} + +/// Consumes at most 1 leading/trailing digit separator and peeks the next +/// value. +macro_rules! peek_lt { + ($self:ident) => { + peek_1!($self, is_lt) + }; +} + +/// Consumes at most 1 digit separator and peeks the next value. +macro_rules! peek_ilt { + ($self:ident) => { + peek_1!($self, is_ilt) + }; +} + +/// Consumes 1 or more leading digit separators and peeks the next value. +macro_rules! peek_lc { + ($self:ident) => { + peek_n!($self, is_lc) + }; +} + +/// Consumes 1 or more internal digit separators and peeks the next value. +macro_rules! peek_ic { + ($self:ident) => { + peek_n!($self, is_ic) + }; +} + +/// Consumes 1 or more trailing digit separators and peeks the next value. +macro_rules! peek_tc { + ($self:ident) => { + peek_n!($self, is_tc) + }; +} + +/// Consumes 1 or more internal/leading digit separators and peeks the next +/// value. +macro_rules! peek_ilc { + ($self:ident) => { + peek_n!($self, is_ilc) + }; +} + +/// Consumes 1 or more internal/trailing digit separators and peeks the next +/// value. +macro_rules! peek_itc { + ($self:ident) => { + peek_n!($self, is_itc) + }; +} + +/// Consumes 1 or more leading/trailing digit separators and peeks the next +/// value. +macro_rules! peek_ltc { + ($self:ident) => { + peek_n!($self, is_ltc) + }; +} + +/// Consumes 1 or more digit separators and peeks the next value. +macro_rules! peek_iltc { + ($self:ident) => { + peek_n!($self, is_iltc) + }; +} + +// AS DIGITS +// --------- + +/// Trait to simplify creation of a `Bytes` object. +pub trait AsBytes<'a> { + /// Create `Bytes` from object. + fn bytes(&'a self) -> Bytes<'a, FORMAT>; +} + +impl<'a> AsBytes<'a> for [u8] { + #[inline(always)] + fn bytes(&'a self) -> Bytes<'a, FORMAT> { + Bytes::new(self) + } +} + +// DIGITS +// ------ + +/// Slice iterator that skips characters matching a given value. +/// +/// This wraps an iterator over a contiguous block of memory, +/// and only returns values that are not equal to skip. +/// +/// The format allows us to dictate the actual behavior of +/// the iterator: in what contexts does it skip digit separators. +/// +/// `FORMAT` is required to tell us what the digit separator is, and where +/// the digit separators are allowed, as well tell us the radix. +/// The radix is required to allow us to differentiate digit from +/// non-digit characters (see [`DigitSeparators`](/docs/DigitSeparators.md) +/// for a detailed explanation on why). +#[derive(Clone)] +pub struct Bytes<'a, const FORMAT: u128> { + /// The raw slice for the iterator. + slc: &'a [u8], + /// Current index of the iterator in the slice. + index: usize, + /// The current count of integer digits returned by the iterator. + /// This is only used if the iterator is not contiguous. + integer_count: usize, + /// The current count of fraction digits returned by the iterator. + /// This is only used if the iterator is not contiguous. + fraction_count: usize, + /// The current count of exponent digits returned by the iterator. + /// This is only used if the iterator is not contiguous. + exponent_count: usize, +} + +impl<'a, const FORMAT: u128> Bytes<'a, FORMAT> { + /// Create new byte object. + #[inline(always)] + pub const fn new(slc: &'a [u8]) -> Self { + Self { + slc, + index: 0, + integer_count: 0, + fraction_count: 0, + exponent_count: 0, + } + } + + /// Initialize the slice from raw parts. + /// + /// # Safety + /// This is safe if and only if the index is <= `slc.len()`. + /// For this reason, since it's easy to get wrong, we only + /// expose it to our `DigitsIterator`s and nothing else. + /// + /// This is only ever used for contiguous iterators. However, + /// it's not guaranteed to only valid for our contiguous + /// iterators. + #[inline(always)] + const unsafe fn from_parts(slc: &'a [u8], index: usize) -> Self { + debug_assert!(index <= slc.len()); + Self { + slc, + index, + integer_count: 0, + fraction_count: 0, + exponent_count: 0, + } + } + + /// Get iterator over integer digits. + #[inline(always)] + pub fn integer_iter<'b>(&'b mut self) -> IntegerDigitsIterator<'a, 'b, FORMAT> { + IntegerDigitsIterator { + byte: self, + } + } + + /// Get iterator over fraction digits. + #[inline(always)] + pub fn fraction_iter<'b>(&'b mut self) -> FractionDigitsIterator<'a, 'b, FORMAT> { + FractionDigitsIterator { + byte: self, + } + } + + /// Get iterator over exponent digits. + #[inline(always)] + pub fn exponent_iter<'b>(&'b mut self) -> ExponentDigitsIterator<'a, 'b, FORMAT> { + ExponentDigitsIterator { + byte: self, + } + } + + /// Get iterator over special floating point values. + #[inline(always)] + pub fn special_iter<'b>(&'b mut self) -> SpecialDigitsIterator<'a, 'b, FORMAT> { + SpecialDigitsIterator { + byte: self, + } + } + + /// Internal implementation that handles if it's contiguous. + /// + /// # Safety + /// + /// Safe if the buffer has at least `N` elements. + #[inline(always)] + unsafe fn step_by_unchecked_impl(&mut self, count: usize, is_contiguous: bool) { + // NOTE: THIS IS NOT a duplicate calling `step_by_unchecked` from a digits + // iterator on the byte, since they can have different contiguousness. + if is_contiguous { + // Contiguous, can skip most of these checks. + debug_assert!(self.as_slice().len() >= count); + } else { + // Since this isn't contiguous, it only works + // if the value is in the range `[0, 1]`. + // We also need to make sure the **current** value + // isn't a digit separator. + let format = NumberFormat::<{ FORMAT }> {}; + debug_assert!(self.as_slice().len() >= count); + debug_assert!(count == 0 || count == 1); + debug_assert!( + count == 0 || self.slc.get(self.index) != Some(&format.digit_separator()) + ); + } + self.index += count; + } + + /// Internal implementation that handles if it's contiguous. + /// + /// If it's contiguous or not does not affect the safety guarantees, + /// however, it can affect correctness. + /// + /// # Safety + /// + /// Safe if the buffer has at least `size_of::` elements. + #[inline(always)] + unsafe fn peek_many_unchecked_impl(&self, is_contiguous: bool) -> V { + // NOTE: THIS IS NOT a duplicate calling `peek_many_unchecked` from a digits + // iterator on the byte, since they can have different contiguousness. + debug_assert!(is_contiguous); + debug_assert!(self.as_slice().len() >= mem::size_of::()); + + let slc = self.as_slice(); + // SAFETY: safe as long as the slice has at least count elements. + unsafe { ptr::read_unaligned::(slc.as_ptr() as *const _) } + } +} + +unsafe impl<'a, const FORMAT: u128> Iter<'a> for Bytes<'a, FORMAT> { + /// If each yielded value is adjacent in memory. + const IS_CONTIGUOUS: bool = NumberFormat::<{ FORMAT }>::DIGIT_SEPARATOR == 0; + + #[inline(always)] + fn get_buffer(&self) -> &'a [u8] { + self.slc + } + + /// Get the current index of the iterator in the slice. + #[inline(always)] + fn cursor(&self) -> usize { + self.index + } + + /// Set the current index of the iterator in the slice. + /// + /// # Safety + /// + /// Safe if `index <= self.buffer_length()`. + #[inline(always)] + unsafe fn set_cursor(&mut self, index: usize) { + debug_assert!(index <= self.buffer_length()); + self.index = index; + } + + /// Get the current number of digits returned by the iterator. + /// + /// For contiguous iterators, this can include the sign character, decimal + /// point, and the exponent sign (that is, it is always the cursor). For + /// non-contiguous iterators, this must always be the only the number of + /// digits returned. + #[inline(always)] + fn current_count(&self) -> usize { + // If the buffer is contiguous, then we don't need to track the + // number of values: the current index is enough. + if Self::IS_CONTIGUOUS { + self.index + } else { + self.integer_count + self.fraction_count + self.exponent_count + } + } + + #[inline(always)] + unsafe fn step_by_unchecked(&mut self, count: usize) { + // SAFETY: Safe if the buffer has at least `N` elements. + unsafe { self.step_by_unchecked_impl(count, Self::IS_CONTIGUOUS) } + } + + #[inline(always)] + unsafe fn peek_many_unchecked(&self) -> V { + // SAFETY: Safe if the buffer has at least `size_of::` elements. + unsafe { self.peek_many_unchecked_impl(Self::IS_CONTIGUOUS) } + } +} + +// ITERATOR HELPERS +// ---------------- + +/// Create skip iterator definition. +macro_rules! skip_iterator { + ($iterator:ident, $doc:literal) => { + #[doc = $doc] + pub struct $iterator<'a: 'b, 'b, const FORMAT: u128> { + /// The internal byte object for the skip iterator. + byte: &'b mut Bytes<'a, FORMAT>, + } + }; +} + +macro_rules! is_sign { + () => { + pub const fn is_sign(&self, value: u8) -> bool { + matches!(value, b'+' | b'-') + } + }; +} + +macro_rules! is_digit_separator { + ($format:ident) => { + /// Determine if the character is a digit separator. + pub const fn is_digit_separator(&self, value: u8) -> bool { + let format = NumberFormat::<{ $format }> {}; + let digit_separator = format.digit_separator(); + if digit_separator == 0 { + // Check at compile time if we have an invalid digit separator. + // b'\x00', or the NUL character, is this invalid value. + false + } else { + value == digit_separator + } + } + }; +} + +/// Create impl block for skip iterator. +macro_rules! skip_iterator_impl { + ($iterator:ident, $radix_cb:ident) => { + impl<'a: 'b, 'b, const FORMAT: u128> $iterator<'a, 'b, FORMAT> { + is_sign!(); + is_digit_separator!(FORMAT); + + /// Create a new digits iterator from the bytes underlying item. + #[inline(always)] + pub fn new(byte: &'b mut Bytes<'a, FORMAT>) -> Self { + Self { + byte, + } + } + + /// Take the first N digits from the iterator. + /// + /// This only takes the digits if we have a contiguous iterator. + /// It takes the digits, validating the bounds, and then advanced + /// the iterators state. It does not support non-contiguous iterators + /// since we would lose information on the count. + #[cfg_attr(not(feature = "compact"), inline(always))] + #[allow(clippy::assertions_on_constants)] // reason="ensuring safety invariants are valid" + pub fn take_n(&mut self, n: usize) -> Option> { + if Self::IS_CONTIGUOUS { + let end = self.byte.slc.len().min(n + self.cursor()); + // NOTE: The compiler should be able to optimize this out. + let slc: &[u8] = &self.byte.slc[..end]; + + // SAFETY: Safe since we just ensured the underlying slice has that count + // elements, so both the underlying slice for this and this **MUST** + // have at least count elements. We do static checking on the bounds for this. + unsafe { + let byte: Bytes<'_, FORMAT> = Bytes::from_parts(slc, self.cursor()); + unsafe { self.set_cursor(end) }; + Some(byte) + } + } else { + None + } + } + } + }; +} + +/// Create impl Iterator block for skip iterator. +macro_rules! skip_iterator_iterator_impl { + ($iterator:ident) => { + impl<'a: 'b, 'b, const FORMAT: u128> Iterator for $iterator<'a, 'b, FORMAT> { + type Item = &'a u8; + + #[inline(always)] + fn next(&mut self) -> Option { + // Peek will handle everything properly internally. + let value = self.peek()?; + // Increment the index so we know not to re-fetch it. + self.byte.index += 1; + // NOTE: Only increment the count if it's not contiguous, otherwise, + // this is an unnecessary performance penalty. We also need + // to check if it's a digit, which adds on additional cost but + // there's not much else we can do. Hopefully the previous inlining + // checks will minimize the performance hit. + if !Self::IS_CONTIGUOUS && self.is_digit(*value) { + self.increment_count(); + } + Some(value) + } + } + }; +} + +/// Create base methods for the Iter block of a skip iterator. +macro_rules! skip_iterator_iter_base { + ($format:ident, $mask:ident, $count:ident) => { + // It's contiguous if we don't skip over any values. + // IE, the digit separator flags for the iterator over + // the digits doesn't skip any values. + const IS_CONTIGUOUS: bool = $format & flags::$mask == 0; + + #[inline(always)] + fn get_buffer(&self) -> &'a [u8] { + self.byte.get_buffer() + } + + #[inline(always)] + fn cursor(&self) -> usize { + self.byte.cursor() + } + + #[inline(always)] + unsafe fn set_cursor(&mut self, index: usize) { + debug_assert!(index <= self.buffer_length()); + // SAFETY: safe if `index <= self.buffer_length()`. + unsafe { self.byte.set_cursor(index) }; + } + + /// Get the current number of digits returned by the iterator. + /// + /// For contiguous iterators, this can include the sign character, decimal + /// point, and the exponent sign (that is, it is always the cursor). For + /// non-contiguous iterators, this must always be the only the number of + /// digits returned. + #[inline(always)] + fn current_count(&self) -> usize { + if Self::IS_CONTIGUOUS { + self.byte.current_count() + } else { + self.byte.$count + } + } + + #[inline(always)] + unsafe fn step_by_unchecked(&mut self, count: usize) { + // SAFETY: Safe if the buffer has at least `N` elements. + unsafe { self.byte.step_by_unchecked_impl(count, Self::IS_CONTIGUOUS) } + } + + #[inline(always)] + unsafe fn peek_many_unchecked(&self) -> V { + // SAFETY: Safe if the buffer has at least `size_of::` elements. + unsafe { self.byte.peek_many_unchecked_impl(Self::IS_CONTIGUOUS) } + } + }; +} + +/// Create base methods for the `DigitsIter` block of a skip iterator. +macro_rules! skip_iterator_digits_iter_base { + () => { + #[inline(always)] + fn is_consumed(&mut self) -> bool { + self.peek().is_none() + } + }; +} + +/// Create impl `ByteIter` block for skip iterator. +macro_rules! skip_iterator_bytesiter_impl { + ($iterator:ident, $mask:ident, $count:ident, $i:ident, $l:ident, $t:ident, $c:ident) => { + unsafe impl<'a: 'b, 'b, const FORMAT: u128> Iter<'a> for $iterator<'a, 'b, FORMAT> { + skip_iterator_iter_base!(FORMAT, $mask, $count); + } + + impl<'a: 'b, 'b, const FORMAT: u128> DigitsIter<'a> for $iterator<'a, 'b, FORMAT> { + skip_iterator_digits_iter_base!(); + + /// Increment the number of digits that have been returned by the iterator. + /// + /// For contiguous iterators, this is a no-op. For non-contiguous iterators, + /// this increments the count by 1. + #[inline(always)] + fn increment_count(&mut self) { + self.byte.$count += 1; + } + + /// Peek the next value of the iterator, without consuming it. + /// + /// Note that this can modify the internal state, by skipping digits + /// for iterators that find the first non-zero value, etc. We optimize + /// this for the case where we have contiguous iterators, since + /// non-contiguous iterators already have a major performance penalty. + /// + /// Checking if the character is a digit in the `next()` implementation + /// after skipping characters can: + /// 1. Likely be optimized out due to the use of macros and inlining. + /// 2. Is a small amount of overhead compared to the branching on + /// characters, + #[inline(always)] + fn peek(&mut self) -> Option<::Item> { + let format = NumberFormat::<{ FORMAT }> {}; + const I: u128 = flags::$i; + const L: u128 = flags::$l; + const T: u128 = flags::$t; + const C: u128 = flags::$c; + const IL: u128 = I | L; + const IT: u128 = I | T; + const LT: u128 = L | T; + const ILT: u128 = I | L | T; + const IC: u128 = I | C; + const LC: u128 = L | C; + const TC: u128 = T | C; + const ILC: u128 = IL | C; + const ITC: u128 = IT | C; + const LTC: u128 = LT | C; + const ILTC: u128 = ILT | C; + + match format.digit_separator_flags() & flags::$mask { + 0 => peek_noskip!(self), + I => peek_i!(self), + L => peek_l!(self), + T => peek_t!(self), + IL => peek_il!(self), + IT => peek_it!(self), + LT => peek_lt!(self), + ILT => peek_ilt!(self), + IC => peek_ic!(self), + LC => peek_lc!(self), + TC => peek_tc!(self), + ILC => peek_ilc!(self), + ITC => peek_itc!(self), + LTC => peek_ltc!(self), + ILTC => peek_iltc!(self), + _ => unreachable!(), + } + } + + /// Determine if the character is a digit. + #[inline(always)] + fn is_digit(&self, value: u8) -> bool { + let format = NumberFormat::<{ FORMAT }> {}; + char_is_digit_const(value, format.mantissa_radix()) + } + } + }; +} + +// INTEGER DIGITS ITERATOR +// ----------------------- + +skip_iterator!(IntegerDigitsIterator, "Iterator that skips over digit separators in the integer."); +skip_iterator_impl!(IntegerDigitsIterator, mantissa_radix); +skip_iterator_iterator_impl!(IntegerDigitsIterator); +skip_iterator_bytesiter_impl!( + IntegerDigitsIterator, + INTEGER_DIGIT_SEPARATOR_FLAG_MASK, + integer_count, + INTEGER_INTERNAL_DIGIT_SEPARATOR, + INTEGER_LEADING_DIGIT_SEPARATOR, + INTEGER_TRAILING_DIGIT_SEPARATOR, + INTEGER_CONSECUTIVE_DIGIT_SEPARATOR +); + +// FRACTION DIGITS ITERATOR +// ------------------------ + +skip_iterator!( + FractionDigitsIterator, + "Iterator that skips over digit separators in the fraction." +); +skip_iterator_impl!(FractionDigitsIterator, mantissa_radix); +skip_iterator_iterator_impl!(FractionDigitsIterator); +skip_iterator_bytesiter_impl!( + FractionDigitsIterator, + FRACTION_DIGIT_SEPARATOR_FLAG_MASK, + fraction_count, + FRACTION_INTERNAL_DIGIT_SEPARATOR, + FRACTION_LEADING_DIGIT_SEPARATOR, + FRACTION_TRAILING_DIGIT_SEPARATOR, + FRACTION_CONSECUTIVE_DIGIT_SEPARATOR +); + +// EXPONENT DIGITS ITERATOR +// ------------------------ + +skip_iterator!( + ExponentDigitsIterator, + "Iterator that skips over digit separators in the exponent." +); +skip_iterator_impl!(ExponentDigitsIterator, exponent_radix); +skip_iterator_iterator_impl!(ExponentDigitsIterator); +skip_iterator_bytesiter_impl!( + ExponentDigitsIterator, + EXPONENT_DIGIT_SEPARATOR_FLAG_MASK, + exponent_count, + EXPONENT_INTERNAL_DIGIT_SEPARATOR, + EXPONENT_LEADING_DIGIT_SEPARATOR, + EXPONENT_TRAILING_DIGIT_SEPARATOR, + EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR +); + +// SPECIAL DIGITS ITERATOR +// ----------------------- + +skip_iterator!( + SpecialDigitsIterator, + "Iterator that skips over digit separators in special floats." +); +skip_iterator_iterator_impl!(SpecialDigitsIterator); + +impl<'a: 'b, 'b, const FORMAT: u128> SpecialDigitsIterator<'a, 'b, FORMAT> { + is_sign!(); + is_digit_separator!(FORMAT); +} + +unsafe impl<'a: 'b, 'b, const FORMAT: u128> Iter<'a> for SpecialDigitsIterator<'a, 'b, FORMAT> { + skip_iterator_iter_base!(FORMAT, SPECIAL_DIGIT_SEPARATOR, integer_count); +} + +impl<'a: 'b, 'b, const FORMAT: u128> DigitsIter<'a> for SpecialDigitsIterator<'a, 'b, FORMAT> { + skip_iterator_digits_iter_base!(); + + // Always a no-op. + #[inline(always)] + fn increment_count(&mut self) { + } + + /// Peek the next value of the iterator, without consuming it. + #[inline(always)] + fn peek(&mut self) -> Option<::Item> { + let format = NumberFormat::<{ FORMAT }> {}; + if format.special_digit_separator() { + peek_iltc!(self) + } else { + peek_noskip!(self) + } + } + + /// Determine if the character is a digit. + #[inline(always)] + fn is_digit(&self, value: u8) -> bool { + let format = NumberFormat::<{ FORMAT }> {}; + char_is_digit_const(value, format.mantissa_radix()) + } +} diff --git a/rust/vendor/lexical-util/src/step.rs b/rust/vendor/lexical-util/src/step.rs new file mode 100644 index 0000000..7515c6f --- /dev/null +++ b/rust/vendor/lexical-util/src/step.rs @@ -0,0 +1,1449 @@ +//! The maximum digits that can be held in a u64 for a given radix without +//! overflowing. +//! +//! This is useful for 128-bit division and operations, since it can +//! reduces the number of inefficient, non-native operations. +//! +//! # Generation +//! +//! See [`etc/step.py`] for the script to generate the divisors and the +//! constants, and the division algorithm. +//! +//! [`etc/step.py`]: https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-util/etc/step.py + +#![cfg(any( + feature = "parse-floats", + feature = "parse-integers", + feature = "write-floats", + feature = "write-integers", +))] + +// NOTE: +// Fallback radixes use 1 for the value to avoid infinite loops, +// but allowing them in `const fn`. + +/// Get the number of digits that can be always processed without overflowing. +/// +/// Calculate the number of digits that can always be processed without +/// overflowing for a given type, that is, it can process every (positive) value +/// in the range `[0, radix^N)` where `N` is the number of digits. +/// +/// For example, with [`u8`] with radix `10`, we have a maximum value of `255`, +/// so we have a min step of `2`: that is, we can always process values from +/// `[0, 10^2)` (or `[0, 100)`). +#[inline(always)] +#[allow(clippy::needless_return)] // reason="required depending on our radix configuration" +pub const fn min_step(radix: u32, bits: usize, is_signed: bool) -> usize { + // NOTE: to avoid branching when w don't need it, we use the compile logic + + #[cfg(feature = "radix")] + { + return match radix { + 2 => min_step_2(bits, is_signed), + 3 => min_step_3(bits, is_signed), + 4 => min_step_4(bits, is_signed), + 5 => min_step_5(bits, is_signed), + 6 => min_step_6(bits, is_signed), + 7 => min_step_7(bits, is_signed), + 8 => min_step_8(bits, is_signed), + 9 => min_step_9(bits, is_signed), + 10 => min_step_10(bits, is_signed), + 11 => min_step_11(bits, is_signed), + 12 => min_step_12(bits, is_signed), + 13 => min_step_13(bits, is_signed), + 14 => min_step_14(bits, is_signed), + 15 => min_step_15(bits, is_signed), + 16 => min_step_16(bits, is_signed), + 17 => min_step_17(bits, is_signed), + 18 => min_step_18(bits, is_signed), + 19 => min_step_19(bits, is_signed), + 20 => min_step_20(bits, is_signed), + 21 => min_step_21(bits, is_signed), + 22 => min_step_22(bits, is_signed), + 23 => min_step_23(bits, is_signed), + 24 => min_step_24(bits, is_signed), + 25 => min_step_25(bits, is_signed), + 26 => min_step_26(bits, is_signed), + 27 => min_step_27(bits, is_signed), + 28 => min_step_28(bits, is_signed), + 29 => min_step_29(bits, is_signed), + 30 => min_step_30(bits, is_signed), + 31 => min_step_31(bits, is_signed), + 32 => min_step_32(bits, is_signed), + 33 => min_step_33(bits, is_signed), + 34 => min_step_34(bits, is_signed), + 35 => min_step_35(bits, is_signed), + 36 => min_step_36(bits, is_signed), + _ => 1, + }; + } + + #[cfg(all(feature = "power-of-two", not(feature = "radix")))] + { + return match radix { + 2 => min_step_2(bits, is_signed), + 4 => min_step_4(bits, is_signed), + 8 => min_step_8(bits, is_signed), + 10 => min_step_10(bits, is_signed), + 16 => min_step_16(bits, is_signed), + 32 => min_step_32(bits, is_signed), + _ => 1, + }; + } + + #[cfg(not(feature = "power-of-two"))] + { + _ = radix; + return min_step_10(bits, is_signed); + } +} + +/// Get the maximum number of digits that can be processed without overflowing. +/// +/// Calculate the number of digits that can be processed without overflowing for +/// a given type, that is, it can process at least `radix^N`. This does not +/// necessarily mean it can process every value in the range `[0, radix^(N+1))`. +/// +/// For example, with [`u8`] with radix `10`, we have a maximum value of `255`, +/// so we have a max step of `3`: that is, it can process up to 3 digits (`[100, +/// 256)`), even if it **cannot** process every 3 digit value (`[256, 1000)`). +#[inline(always)] +#[allow(clippy::needless_return)] // reason="required depending on our radix configuration" +pub const fn max_step(radix: u32, bits: usize, is_signed: bool) -> usize { + #[cfg(feature = "radix")] + { + return match radix { + 2 => max_step_2(bits, is_signed), + 3 => max_step_3(bits, is_signed), + 4 => max_step_4(bits, is_signed), + 5 => max_step_5(bits, is_signed), + 6 => max_step_6(bits, is_signed), + 7 => max_step_7(bits, is_signed), + 8 => max_step_8(bits, is_signed), + 9 => max_step_9(bits, is_signed), + 10 => max_step_10(bits, is_signed), + 11 => max_step_11(bits, is_signed), + 12 => max_step_12(bits, is_signed), + 13 => max_step_13(bits, is_signed), + 14 => max_step_14(bits, is_signed), + 15 => max_step_15(bits, is_signed), + 16 => max_step_16(bits, is_signed), + 17 => max_step_17(bits, is_signed), + 18 => max_step_18(bits, is_signed), + 19 => max_step_19(bits, is_signed), + 20 => max_step_20(bits, is_signed), + 21 => max_step_21(bits, is_signed), + 22 => max_step_22(bits, is_signed), + 23 => max_step_23(bits, is_signed), + 24 => max_step_24(bits, is_signed), + 25 => max_step_25(bits, is_signed), + 26 => max_step_26(bits, is_signed), + 27 => max_step_27(bits, is_signed), + 28 => max_step_28(bits, is_signed), + 29 => max_step_29(bits, is_signed), + 30 => max_step_30(bits, is_signed), + 31 => max_step_31(bits, is_signed), + 32 => max_step_32(bits, is_signed), + 33 => max_step_33(bits, is_signed), + 34 => max_step_34(bits, is_signed), + 35 => max_step_35(bits, is_signed), + 36 => max_step_36(bits, is_signed), + _ => 1, + }; + } + + #[cfg(all(feature = "power-of-two", not(feature = "radix")))] + { + return match radix { + 2 => max_step_2(bits, is_signed), + 4 => max_step_4(bits, is_signed), + 8 => max_step_8(bits, is_signed), + 10 => max_step_10(bits, is_signed), + 16 => max_step_16(bits, is_signed), + 32 => max_step_32(bits, is_signed), + _ => 1, + }; + } + + #[cfg(not(feature = "power-of-two"))] + { + _ = radix; + return max_step_10(bits, is_signed); + } +} + +/// Calculate the number of digits that can be processed without overflowing a +/// [`u64`]. Helper function since this is used for 128-bit division. +/// +/// This is an alias for [`min_step`] with `bits == 64` and `is_signed == +/// false`. +pub const fn u64_step(radix: u32) -> usize { + min_step(radix, 64, false) +} + +// AUTO-GENERATED +// These functions were auto-generated by `etc/step.py`. +// Do not edit them unless there is a good reason to. +// Preferably, edit the source code to generate the constants. +// +// NOTE: For the fallthrough value for types (in case of adding short +// or wider type support in the future), use 1 so it doesn't infinitely +// recurse. Under normal circumstances, this will never be called. + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn max_step_2(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 7, + 8 if !is_signed => 8, + 16 if is_signed => 15, + 16 if !is_signed => 16, + 32 if is_signed => 31, + 32 if !is_signed => 32, + 64 if is_signed => 63, + 64 if !is_signed => 64, + 128 if is_signed => 127, + 128 if !is_signed => 128, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn min_step_2(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 7, + 8 if !is_signed => 8, + 16 if is_signed => 15, + 16 if !is_signed => 16, + 32 if is_signed => 31, + 32 if !is_signed => 32, + 64 if is_signed => 63, + 64 if !is_signed => 64, + 128 if is_signed => 127, + 128 if !is_signed => 128, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_3(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 5, + 8 if !is_signed => 6, + 16 if is_signed => 10, + 16 if !is_signed => 11, + 32 if is_signed => 20, + 32 if !is_signed => 21, + 64 if is_signed => 40, + 64 if !is_signed => 41, + 128 if is_signed => 81, + 128 if !is_signed => 81, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_3(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 4, + 8 if !is_signed => 5, + 16 if is_signed => 9, + 16 if !is_signed => 10, + 32 if is_signed => 19, + 32 if !is_signed => 20, + 64 if is_signed => 39, + 64 if !is_signed => 40, + 128 if is_signed => 80, + 128 if !is_signed => 80, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn max_step_4(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 4, + 8 if !is_signed => 4, + 16 if is_signed => 8, + 16 if !is_signed => 8, + 32 if is_signed => 16, + 32 if !is_signed => 16, + 64 if is_signed => 32, + 64 if !is_signed => 32, + 128 if is_signed => 64, + 128 if !is_signed => 64, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn min_step_4(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 3, + 8 if !is_signed => 4, + 16 if is_signed => 7, + 16 if !is_signed => 8, + 32 if is_signed => 15, + 32 if !is_signed => 16, + 64 if is_signed => 31, + 64 if !is_signed => 32, + 128 if is_signed => 63, + 128 if !is_signed => 64, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_5(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 4, + 8 if !is_signed => 4, + 16 if is_signed => 7, + 16 if !is_signed => 7, + 32 if is_signed => 14, + 32 if !is_signed => 14, + 64 if is_signed => 28, + 64 if !is_signed => 28, + 128 if is_signed => 55, + 128 if !is_signed => 56, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_5(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 3, + 8 if !is_signed => 3, + 16 if is_signed => 6, + 16 if !is_signed => 6, + 32 if is_signed => 13, + 32 if !is_signed => 13, + 64 if is_signed => 27, + 64 if !is_signed => 27, + 128 if is_signed => 54, + 128 if !is_signed => 55, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_6(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 3, + 8 if !is_signed => 4, + 16 if is_signed => 6, + 16 if !is_signed => 7, + 32 if is_signed => 12, + 32 if !is_signed => 13, + 64 if is_signed => 25, + 64 if !is_signed => 25, + 128 if is_signed => 50, + 128 if !is_signed => 50, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_6(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 3, + 16 if is_signed => 5, + 16 if !is_signed => 6, + 32 if is_signed => 11, + 32 if !is_signed => 12, + 64 if is_signed => 24, + 64 if !is_signed => 24, + 128 if is_signed => 49, + 128 if !is_signed => 49, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_7(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 3, + 8 if !is_signed => 3, + 16 if is_signed => 6, + 16 if !is_signed => 6, + 32 if is_signed => 12, + 32 if !is_signed => 12, + 64 if is_signed => 23, + 64 if !is_signed => 23, + 128 if is_signed => 46, + 128 if !is_signed => 46, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_7(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 5, + 16 if !is_signed => 5, + 32 if is_signed => 11, + 32 if !is_signed => 11, + 64 if is_signed => 22, + 64 if !is_signed => 22, + 128 if is_signed => 45, + 128 if !is_signed => 45, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn max_step_8(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 3, + 8 if !is_signed => 3, + 16 if is_signed => 5, + 16 if !is_signed => 6, + 32 if is_signed => 11, + 32 if !is_signed => 11, + 64 if is_signed => 21, + 64 if !is_signed => 22, + 128 if is_signed => 43, + 128 if !is_signed => 43, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn min_step_8(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 5, + 16 if !is_signed => 5, + 32 if is_signed => 10, + 32 if !is_signed => 10, + 64 if is_signed => 21, + 64 if !is_signed => 21, + 128 if is_signed => 42, + 128 if !is_signed => 42, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_9(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 3, + 8 if !is_signed => 3, + 16 if is_signed => 5, + 16 if !is_signed => 6, + 32 if is_signed => 10, + 32 if !is_signed => 11, + 64 if is_signed => 20, + 64 if !is_signed => 21, + 128 if is_signed => 41, + 128 if !is_signed => 41, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_9(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 5, + 32 if is_signed => 9, + 32 if !is_signed => 10, + 64 if is_signed => 19, + 64 if !is_signed => 20, + 128 if is_signed => 40, + 128 if !is_signed => 40, + _ => 1, + } +} + +#[inline(always)] +const fn max_step_10(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 3, + 8 if !is_signed => 3, + 16 if is_signed => 5, + 16 if !is_signed => 5, + 32 if is_signed => 10, + 32 if !is_signed => 10, + 64 if is_signed => 19, + 64 if !is_signed => 20, + 128 if is_signed => 39, + 128 if !is_signed => 39, + _ => 1, + } +} + +#[inline(always)] +const fn min_step_10(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 9, + 32 if !is_signed => 9, + 64 if is_signed => 18, + 64 if !is_signed => 19, + 128 if is_signed => 38, + 128 if !is_signed => 38, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_11(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 3, + 8 if !is_signed => 3, + 16 if is_signed => 5, + 16 if !is_signed => 5, + 32 if is_signed => 9, + 32 if !is_signed => 10, + 64 if is_signed => 19, + 64 if !is_signed => 19, + 128 if is_signed => 37, + 128 if !is_signed => 38, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_11(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 8, + 32 if !is_signed => 9, + 64 if is_signed => 18, + 64 if !is_signed => 18, + 128 if is_signed => 36, + 128 if !is_signed => 37, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_12(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 3, + 16 if is_signed => 5, + 16 if !is_signed => 5, + 32 if is_signed => 9, + 32 if !is_signed => 9, + 64 if is_signed => 18, + 64 if !is_signed => 18, + 128 if is_signed => 36, + 128 if !is_signed => 36, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_12(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 8, + 32 if !is_signed => 8, + 64 if is_signed => 17, + 64 if !is_signed => 17, + 128 if is_signed => 35, + 128 if !is_signed => 35, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_13(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 3, + 16 if is_signed => 5, + 16 if !is_signed => 5, + 32 if is_signed => 9, + 32 if !is_signed => 9, + 64 if is_signed => 18, + 64 if !is_signed => 18, + 128 if is_signed => 35, + 128 if !is_signed => 35, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_13(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 8, + 32 if !is_signed => 8, + 64 if is_signed => 17, + 64 if !is_signed => 17, + 128 if is_signed => 34, + 128 if !is_signed => 34, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_14(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 3, + 16 if is_signed => 4, + 16 if !is_signed => 5, + 32 if is_signed => 9, + 32 if !is_signed => 9, + 64 if is_signed => 17, + 64 if !is_signed => 17, + 128 if is_signed => 34, + 128 if !is_signed => 34, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_14(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 2, + 16 if is_signed => 3, + 16 if !is_signed => 4, + 32 if is_signed => 8, + 32 if !is_signed => 8, + 64 if is_signed => 16, + 64 if !is_signed => 16, + 128 if is_signed => 33, + 128 if !is_signed => 33, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_15(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 3, + 16 if is_signed => 4, + 16 if !is_signed => 5, + 32 if is_signed => 8, + 32 if !is_signed => 9, + 64 if is_signed => 17, + 64 if !is_signed => 17, + 128 if is_signed => 33, + 128 if !is_signed => 33, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_15(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 2, + 16 if is_signed => 3, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 8, + 64 if is_signed => 16, + 64 if !is_signed => 16, + 128 if is_signed => 32, + 128 if !is_signed => 32, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn max_step_16(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 8, + 32 if !is_signed => 8, + 64 if is_signed => 16, + 64 if !is_signed => 16, + 128 if is_signed => 32, + 128 if !is_signed => 32, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn min_step_16(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 2, + 16 if is_signed => 3, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 8, + 64 if is_signed => 15, + 64 if !is_signed => 16, + 128 if is_signed => 31, + 128 if !is_signed => 32, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_17(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 8, + 32 if !is_signed => 8, + 64 if is_signed => 16, + 64 if !is_signed => 16, + 128 if is_signed => 32, + 128 if !is_signed => 32, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_17(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 15, + 64 if !is_signed => 15, + 128 if is_signed => 31, + 128 if !is_signed => 31, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_18(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 8, + 32 if !is_signed => 8, + 64 if is_signed => 16, + 64 if !is_signed => 16, + 128 if is_signed => 31, + 128 if !is_signed => 31, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_18(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 15, + 64 if !is_signed => 15, + 128 if is_signed => 30, + 128 if !is_signed => 30, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_19(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 8, + 32 if !is_signed => 8, + 64 if is_signed => 15, + 64 if !is_signed => 16, + 128 if is_signed => 30, + 128 if !is_signed => 31, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_19(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 14, + 64 if !is_signed => 15, + 128 if is_signed => 29, + 128 if !is_signed => 30, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_20(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 8, + 32 if !is_signed => 8, + 64 if is_signed => 15, + 64 if !is_signed => 15, + 128 if is_signed => 30, + 128 if !is_signed => 30, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_20(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 14, + 64 if !is_signed => 14, + 128 if is_signed => 29, + 128 if !is_signed => 29, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_21(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 8, + 32 if !is_signed => 8, + 64 if is_signed => 15, + 64 if !is_signed => 15, + 128 if is_signed => 29, + 128 if !is_signed => 30, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_21(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 14, + 64 if !is_signed => 14, + 128 if is_signed => 28, + 128 if !is_signed => 29, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_22(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 8, + 64 if is_signed => 15, + 64 if !is_signed => 15, + 128 if is_signed => 29, + 128 if !is_signed => 29, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_22(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 7, + 64 if is_signed => 14, + 64 if !is_signed => 14, + 128 if is_signed => 28, + 128 if !is_signed => 28, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_23(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 8, + 64 if is_signed => 14, + 64 if !is_signed => 15, + 128 if is_signed => 29, + 128 if !is_signed => 29, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_23(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 7, + 64 if is_signed => 13, + 64 if !is_signed => 14, + 128 if is_signed => 28, + 128 if !is_signed => 28, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_24(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 14, + 64 if !is_signed => 14, + 128 if is_signed => 28, + 128 if !is_signed => 28, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_24(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 13, + 64 if !is_signed => 13, + 128 if is_signed => 27, + 128 if !is_signed => 27, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_25(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 14, + 64 if !is_signed => 14, + 128 if is_signed => 28, + 128 if !is_signed => 28, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_25(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 13, + 64 if !is_signed => 13, + 128 if is_signed => 27, + 128 if !is_signed => 27, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_26(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 14, + 64 if !is_signed => 14, + 128 if is_signed => 28, + 128 if !is_signed => 28, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_26(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 13, + 64 if !is_signed => 13, + 128 if is_signed => 27, + 128 if !is_signed => 27, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_27(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 14, + 64 if !is_signed => 14, + 128 if is_signed => 27, + 128 if !is_signed => 27, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_27(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 13, + 64 if !is_signed => 13, + 128 if is_signed => 26, + 128 if !is_signed => 26, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_28(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 14, + 64 if !is_signed => 14, + 128 if is_signed => 27, + 128 if !is_signed => 27, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_28(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 13, + 64 if !is_signed => 13, + 128 if is_signed => 26, + 128 if !is_signed => 26, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_29(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 13, + 64 if !is_signed => 14, + 128 if is_signed => 27, + 128 if !is_signed => 27, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_29(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 12, + 64 if !is_signed => 13, + 128 if is_signed => 26, + 128 if !is_signed => 26, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_30(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 13, + 64 if !is_signed => 14, + 128 if is_signed => 26, + 128 if !is_signed => 27, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_30(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 12, + 64 if !is_signed => 13, + 128 if is_signed => 25, + 128 if !is_signed => 26, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_31(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 4, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 13, + 64 if !is_signed => 13, + 128 if is_signed => 26, + 128 if !is_signed => 26, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_31(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 12, + 64 if !is_signed => 12, + 128 if is_signed => 25, + 128 if !is_signed => 25, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn max_step_32(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 3, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 13, + 64 if !is_signed => 13, + 128 if is_signed => 26, + 128 if !is_signed => 26, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "power-of-two"), allow(dead_code))] +const fn min_step_32(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 3, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 12, + 64 if !is_signed => 12, + 128 if is_signed => 25, + 128 if !is_signed => 25, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_33(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 3, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 13, + 64 if !is_signed => 13, + 128 if is_signed => 26, + 128 if !is_signed => 26, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_33(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 2, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 12, + 64 if !is_signed => 12, + 128 if is_signed => 25, + 128 if !is_signed => 25, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_34(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 3, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 13, + 64 if !is_signed => 13, + 128 if is_signed => 25, + 128 if !is_signed => 26, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_34(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 2, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 12, + 64 if !is_signed => 12, + 128 if is_signed => 24, + 128 if !is_signed => 25, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_35(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 3, + 16 if !is_signed => 4, + 32 if is_signed => 7, + 32 if !is_signed => 7, + 64 if is_signed => 13, + 64 if !is_signed => 13, + 128 if is_signed => 25, + 128 if !is_signed => 25, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_35(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 2, + 16 if !is_signed => 3, + 32 if is_signed => 6, + 32 if !is_signed => 6, + 64 if is_signed => 12, + 64 if !is_signed => 12, + 128 if is_signed => 24, + 128 if !is_signed => 24, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn max_step_36(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 2, + 8 if !is_signed => 2, + 16 if is_signed => 3, + 16 if !is_signed => 4, + 32 if is_signed => 6, + 32 if !is_signed => 7, + 64 if is_signed => 13, + 64 if !is_signed => 13, + 128 if is_signed => 25, + 128 if !is_signed => 25, + _ => 1, + } +} + +#[inline(always)] +#[cfg_attr(not(feature = "radix"), allow(dead_code))] +const fn min_step_36(bits: usize, is_signed: bool) -> usize { + match bits { + 8 if is_signed => 1, + 8 if !is_signed => 1, + 16 if is_signed => 2, + 16 if !is_signed => 3, + 32 if is_signed => 5, + 32 if !is_signed => 6, + 64 if is_signed => 12, + 64 if !is_signed => 12, + 128 if is_signed => 24, + 128 if !is_signed => 24, + _ => 1, + } +} diff --git a/rust/vendor/lexical-util/tests/algorithm_tests.rs b/rust/vendor/lexical-util/tests/algorithm_tests.rs new file mode 100644 index 0000000..e1c9b20 --- /dev/null +++ b/rust/vendor/lexical-util/tests/algorithm_tests.rs @@ -0,0 +1,30 @@ +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +use lexical_util::algorithm; + +#[test] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +fn copy_to_dest_test() { + let src = b"12345"; + let mut dst = [b'0'; 16]; + + assert_eq!(5, algorithm::copy_to_dst(&mut dst, src)); + assert_eq!(&dst[..5], src); +} + +#[test] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +fn ltrim_char_test() { + let w = "0001"; + let x = "1010"; + let y = "1.00"; + let z = "1e05"; + + assert_eq!(algorithm::ltrim_char_count(w.as_bytes(), b'0'), 3); + assert_eq!(algorithm::ltrim_char_count(x.as_bytes(), b'0'), 0); + assert_eq!(algorithm::ltrim_char_count(x.as_bytes(), b'1'), 1); + assert_eq!(algorithm::ltrim_char_count(y.as_bytes(), b'0'), 0); + assert_eq!(algorithm::ltrim_char_count(y.as_bytes(), b'1'), 1); + assert_eq!(algorithm::ltrim_char_count(z.as_bytes(), b'0'), 0); + assert_eq!(algorithm::ltrim_char_count(z.as_bytes(), b'1'), 1); + assert_eq!(algorithm::ltrim_char_count(z.as_bytes(), b'5'), 0); +} diff --git a/rust/vendor/lexical-util/tests/ascii_tests.rs b/rust/vendor/lexical-util/tests/ascii_tests.rs new file mode 100644 index 0000000..7a6f2dc --- /dev/null +++ b/rust/vendor/lexical-util/tests/ascii_tests.rs @@ -0,0 +1,51 @@ +use lexical_util::ascii; + +#[test] +fn is_valid_ascii_test() { + assert_eq!(ascii::is_valid_ascii(b'\x00'), false); + assert_eq!(ascii::is_valid_ascii(b'\n'), true); + assert_eq!(ascii::is_valid_ascii(b'\r'), true); + assert_eq!(ascii::is_valid_ascii(b'\x1b'), false); + assert_eq!(ascii::is_valid_ascii(b' '), true); + assert_eq!(ascii::is_valid_ascii(b'0'), true); + assert_eq!(ascii::is_valid_ascii(b'9'), true); + assert_eq!(ascii::is_valid_ascii(b':'), true); + assert_eq!(ascii::is_valid_ascii(b'A'), true); + assert_eq!(ascii::is_valid_ascii(b'Z'), true); + assert_eq!(ascii::is_valid_ascii(b']'), true); + assert_eq!(ascii::is_valid_ascii(b'a'), true); + assert_eq!(ascii::is_valid_ascii(b'z'), true); + assert_eq!(ascii::is_valid_ascii(b'~'), true); + assert_eq!(ascii::is_valid_ascii(b'\x7f'), false); +} + +#[test] +fn is_valid_ascii_slice_test() { + assert_eq!(ascii::is_valid_ascii_slice(b" 09a"), true); + assert_eq!(ascii::is_valid_ascii_slice(b" 09a\x1b"), false); +} + +#[test] +fn is_valid_letter_test() { + assert_eq!(ascii::is_valid_letter(b'\x00'), false); + assert_eq!(ascii::is_valid_letter(b'\n'), false); + assert_eq!(ascii::is_valid_letter(b'\r'), false); + assert_eq!(ascii::is_valid_letter(b'\x1b'), false); + assert_eq!(ascii::is_valid_letter(b' '), false); + assert_eq!(ascii::is_valid_letter(b'0'), false); + assert_eq!(ascii::is_valid_letter(b'9'), false); + assert_eq!(ascii::is_valid_letter(b':'), false); + assert_eq!(ascii::is_valid_letter(b'A'), true); + assert_eq!(ascii::is_valid_letter(b'Z'), true); + assert_eq!(ascii::is_valid_letter(b']'), false); + assert_eq!(ascii::is_valid_letter(b'a'), true); + assert_eq!(ascii::is_valid_letter(b'z'), true); + assert_eq!(ascii::is_valid_letter(b'~'), false); + assert_eq!(ascii::is_valid_letter(b'\x7f'), false); +} + +#[test] +fn is_valid_letter_slice_test() { + assert_eq!(ascii::is_valid_letter_slice(b" 09a"), false); + assert_eq!(ascii::is_valid_letter_slice(b"aZAz"), true); +} diff --git a/rust/vendor/lexical-util/tests/bf16_tests.rs b/rust/vendor/lexical-util/tests/bf16_tests.rs new file mode 100644 index 0000000..e193a26 --- /dev/null +++ b/rust/vendor/lexical-util/tests/bf16_tests.rs @@ -0,0 +1,44 @@ +#![cfg(feature = "f16")] + +use lexical_util::bf16::bf16; +use lexical_util::num::Float; + +#[test] +fn as_f32_test() { + assert_eq!(bf16::from_bits(1).as_f32(), 9.18355e-41f32); + assert_eq!(bf16::ZERO.as_f32_const(), 0.0f32); + assert_eq!(bf16::ZERO.to_bits(), 0); + assert_eq!(bf16::ONE.as_f32_const(), 1.0f32); + assert_eq!(bf16::ONE.to_bits(), (127 << 7)); + assert_eq!(bf16::TWO.as_f32_const(), 2.0f32); + assert_eq!(bf16::TWO.to_bits(), (128 << 7)); + assert_eq!(bf16::from_bits(126 << 7).as_f32_const(), 0.5f32); + assert!(bf16::NAN.as_f32_const().is_nan()); + assert!(bf16::INFINITY.as_f32_const().is_inf()); + assert!(bf16::NEG_INFINITY.as_f32_const().is_inf()); +} + +#[test] +fn from_f32_test() { + assert_eq!(bf16::from_f32_const(4.5917e-41f32).to_bits(), 0); + assert_eq!(bf16::from_f32_const(4.5918e-41f32).to_bits(), 0); + assert_eq!(bf16::from_f32_const(4.5919e-41f32).to_bits(), 1); + assert_eq!(bf16::from_f32_const(9.18354e-41f32).to_bits(), 1); + assert_eq!(bf16::from_f32_const(9.18355e-41f32).to_bits(), 1); + assert_eq!(bf16::from_f32_const(9.18356e-41f32).to_bits(), 1); + assert_eq!(bf16::from_f32_const(1.37752e-40f32).to_bits(), 1); + assert_eq!(bf16::from_f32_const(1.37753e-40f32).to_bits(), 2); + assert_eq!(bf16::from_f32_const(1.37754e-40f32).to_bits(), 2); + assert!(bf16::from_f32_const(f32::NAN).is_nan()); + assert!(bf16::from_f32_const(f32::INFINITY).is_inf()); + assert!(bf16::from_f32_const(f32::NEG_INFINITY).is_inf()); +} + +#[test] +fn math_tests() { + assert_eq!(bf16::ONE + bf16::ONE, bf16::TWO); + assert_eq!(bf16::ONE * bf16::ONE, bf16::ONE); + assert_eq!(bf16::ONE / bf16::ONE, bf16::ONE); + assert_eq!(bf16::ONE - bf16::ONE, bf16::ZERO); + assert_eq!(bf16::ONE % bf16::ONE, bf16::ZERO); +} diff --git a/rust/vendor/lexical-util/tests/digit_tests.rs b/rust/vendor/lexical-util/tests/digit_tests.rs new file mode 100644 index 0000000..9b7bdfd --- /dev/null +++ b/rust/vendor/lexical-util/tests/digit_tests.rs @@ -0,0 +1,115 @@ +#![cfg(any( + feature = "parse-floats", + feature = "parse-integers", + feature = "write-floats", + feature = "write-integers", +))] + +use lexical_util::digit; + +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] +fn char_to_digit(c: u8, radix: u32, expected: Option) { + assert_eq!(digit::char_to_digit_const(c, radix), expected); + assert_eq!(digit::char_to_digit(c, radix), expected); +} + +#[test] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] +fn char_to_digit_test() { + char_to_digit(b'0', 2, Some(0)); + char_to_digit(b'1', 2, Some(1)); + char_to_digit(b'9', 2, None); + char_to_digit(b'A', 2, None); + char_to_digit(b'Z', 2, None); + + char_to_digit(b'0', 10, Some(0)); + char_to_digit(b'1', 10, Some(1)); + char_to_digit(b'9', 10, Some(9)); + char_to_digit(b'A', 10, None); + char_to_digit(b'Z', 10, None); + + char_to_digit(b'0', 16, Some(0)); + char_to_digit(b'1', 16, Some(1)); + char_to_digit(b'9', 16, Some(9)); + char_to_digit(b'A', 16, Some(10)); + char_to_digit(b'Z', 16, None); + + // char_to_digit doesn't care about the radix. + // check some more comprehensive cases. + for c in b'0'..=b'9' { + char_to_digit(c, 10, Some(c as u32 - b'0' as u32)); + } + char_to_digit(0x29, 10, None); + char_to_digit(0x3A, 10, None); + char_to_digit(0x59, 10, None); + + for c in b'0'..=b'8' { + char_to_digit(c, 9, Some(c as u32 - b'0' as u32)); + } + char_to_digit(0x29, 9, None); + char_to_digit(0x39, 9, None); + char_to_digit(0x3A, 9, None); + char_to_digit(0x59, 9, None); + + for c in b'0'..=b'9' { + char_to_digit(c, 16, Some(c as u32 - b'0' as u32)); + } + for c in b'a'..=b'f' { + char_to_digit(c, 16, Some(c as u32 - b'a' as u32 + 10)); + } + for c in b'A'..=b'F' { + char_to_digit(c, 16, Some(c as u32 - b'A' as u32 + 10)); + } + char_to_digit(0x29, 16, None); + char_to_digit(0x40, 16, None); + char_to_digit(0x3A, 16, None); + char_to_digit(0x59, 16, None); + char_to_digit(0x41, 16, Some(10)); + char_to_digit(0x47, 16, None); + char_to_digit(0x5A, 16, None); + char_to_digit(0x61, 16, Some(10)); + char_to_digit(0x67, 16, None); + char_to_digit(0x7A, 16, None); +} + +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] +fn char_is_digit(c: u8, radix: u32, expected: bool) { + assert_eq!(digit::char_is_digit_const(c, radix), expected); + assert_eq!(digit::char_is_digit(c, radix), expected); +} + +#[test] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] +fn char_is_digit_const_test() { + char_is_digit(b'0', 2, true); + char_is_digit(b'1', 2, true); + char_is_digit(b'9', 2, false); + char_is_digit(b'A', 2, false); + char_is_digit(b'Z', 2, false); + + char_is_digit(b'0', 10, true); + char_is_digit(b'1', 10, true); + char_is_digit(b'9', 10, true); + char_is_digit(b'A', 10, false); + char_is_digit(b'Z', 10, false); + + char_is_digit(b'0', 16, true); + char_is_digit(b'1', 16, true); + char_is_digit(b'9', 16, true); + char_is_digit(b'A', 16, true); + char_is_digit(b'Z', 16, false); +} + +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +fn digit_to_char(digit: u32, radix: u32, expected: u8) { + assert_eq!(digit::digit_to_char_const(digit, radix), expected); + assert_eq!(digit::digit_to_char(digit), expected); +} + +#[test] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] +fn digit_to_char_const_test() { + digit_to_char(9, 10, b'9'); + digit_to_char(10, 36, b'A'); + digit_to_char(11, 36, b'B'); +} diff --git a/rust/vendor/lexical-util/tests/f16_tests.rs b/rust/vendor/lexical-util/tests/f16_tests.rs new file mode 100644 index 0000000..3979812 --- /dev/null +++ b/rust/vendor/lexical-util/tests/f16_tests.rs @@ -0,0 +1,41 @@ +#![cfg(feature = "f16")] + +use lexical_util::f16::f16; +use lexical_util::num::Float; + +#[test] +fn as_f32_test() { + assert_eq!(f16::from_bits(1).as_f32_const(), 0.000000059604645); + assert_eq!(f16::ZERO.as_f32_const(), 0.0f32); + assert_eq!(f16::ZERO.to_bits(), 0); + assert_eq!(f16::ONE.as_f32_const(), 1.0f32); + assert_eq!(f16::ONE.to_bits(), (15 << 10)); + assert_eq!(f16::TWO.as_f32_const(), 2.0f32); + assert_eq!(f16::TWO.to_bits(), (16 << 10)); + assert_eq!(f16::from_bits(14 << 10).as_f32_const(), 0.5f32); + assert!(f16::NAN.as_f32_const().is_nan()); + assert!(f16::INFINITY.as_f32_const().is_inf()); + assert!(f16::NEG_INFINITY.as_f32_const().is_inf()); +} + +#[test] +fn from_f32_test() { + assert_eq!(f16::from_f32_const(2.980232e-08).to_bits(), 0); + assert_eq!(f16::from_f32_const(2.9802322e-08).to_bits(), 0); + assert_eq!(f16::from_f32_const(2.9802326e-08).to_bits(), 1); + assert_eq!(f16::from_f32_const(5.960464e-08).to_bits(), 1); + assert_eq!(f16::from_f32_const(5.9604645e-08).to_bits(), 1); + assert_eq!(f16::from_f32_const(5.960465e-08).to_bits(), 1); + assert!(f16::from_f32_const(f32::NAN).is_nan()); + assert!(f16::from_f32_const(f32::INFINITY).is_inf()); + assert!(f16::from_f32_const(f32::NEG_INFINITY).is_inf()); +} + +#[test] +fn math_tests() { + assert_eq!(f16::ONE + f16::ONE, f16::TWO); + assert_eq!(f16::ONE * f16::ONE, f16::ONE); + assert_eq!(f16::ONE / f16::ONE, f16::ONE); + assert_eq!(f16::ONE - f16::ONE, f16::ZERO); + assert_eq!(f16::ONE % f16::ONE, f16::ZERO); +} diff --git a/rust/vendor/lexical-util/tests/feature_format_tests.rs b/rust/vendor/lexical-util/tests/feature_format_tests.rs new file mode 100644 index 0000000..ead3fc5 --- /dev/null +++ b/rust/vendor/lexical-util/tests/feature_format_tests.rs @@ -0,0 +1,241 @@ +#![cfg(feature = "format")] + +use core::num; + +use lexical_util::format; + +#[test] +fn ignore_test() { + let fmt = format::NumberFormat::<{ format::IGNORE }> {}; + assert_eq!(fmt.flags(), format::DIGIT_SEPARATOR_FLAG_MASK); + assert_eq!(fmt.digit_separator(), b'_'); + assert_eq!(fmt.required_integer_digits(), false); + assert_eq!(fmt.required_fraction_digits(), false); + assert_eq!(fmt.required_exponent_digits(), false); + assert_eq!(fmt.required_mantissa_digits(), false); + assert_eq!(fmt.required_digits(), false); + assert_eq!(fmt.no_positive_mantissa_sign(), false); + assert_eq!(fmt.required_mantissa_sign(), false); + assert_eq!(fmt.no_exponent_notation(), false); + assert_eq!(fmt.no_positive_exponent_sign(), false); + assert_eq!(fmt.required_exponent_sign(), false); + assert_eq!(fmt.no_exponent_without_fraction(), false); + assert_eq!(fmt.no_special(), false); + assert_eq!(fmt.case_sensitive_special(), false); + assert_eq!(fmt.no_integer_leading_zeros(), false); + assert_eq!(fmt.no_float_leading_zeros(), false); + assert_eq!(fmt.required_exponent_notation(), false); + assert_eq!(fmt.case_sensitive_exponent(), false); + #[cfg(feature = "power-of-two")] + assert_eq!(fmt.case_sensitive_base_prefix(), false); + #[cfg(feature = "power-of-two")] + assert_eq!(fmt.case_sensitive_base_suffix(), false); + assert_eq!(fmt.integer_internal_digit_separator(), true); + assert_eq!(fmt.fraction_internal_digit_separator(), true); + assert_eq!(fmt.exponent_internal_digit_separator(), true); + assert_eq!(fmt.internal_digit_separator(), true); + assert_eq!(fmt.integer_leading_digit_separator(), true); + assert_eq!(fmt.fraction_leading_digit_separator(), true); + assert_eq!(fmt.exponent_leading_digit_separator(), true); + assert_eq!(fmt.leading_digit_separator(), true); + assert_eq!(fmt.integer_trailing_digit_separator(), true); + assert_eq!(fmt.fraction_trailing_digit_separator(), true); + assert_eq!(fmt.exponent_trailing_digit_separator(), true); + assert_eq!(fmt.trailing_digit_separator(), true); + assert_eq!(fmt.integer_consecutive_digit_separator(), true); + assert_eq!(fmt.fraction_consecutive_digit_separator(), true); + assert_eq!(fmt.exponent_consecutive_digit_separator(), true); + assert_eq!(fmt.consecutive_digit_separator(), true); + assert_eq!(fmt.special_digit_separator(), true); +} + +fn test_flag() { + let fmt = format::NumberFormat:: {}; + assert_eq!(fmt.flags(), FORMAT); + assert_eq!(fmt.digit_separator(), 0); +} + +macro_rules! test_flag { + ($field:ident, $flag:ident) => {{ + test_flag::<{ format::$flag }>(); + + if format::$flag & format::CONSECUTIVE_DIGIT_SEPARATOR == 0 { + const FORMAT: u128 = format::NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .$field(true) + .build_unchecked(); + let fmt = format::NumberFormat:: {}; + assert_eq!(fmt.is_valid(), true); + assert_eq!(fmt.$field(), true); + } else { + const FORMAT: u128 = format::NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .internal_digit_separator(true) + .leading_digit_separator(true) + .trailing_digit_separator(true) + .$field(true) + .build_unchecked(); + let fmt = format::NumberFormat:: {}; + assert_eq!(fmt.is_valid(), true); + assert_eq!(fmt.$field(), true); + } + }}; +} + +#[test] +fn flags_test() { + test_flag!(required_integer_digits, REQUIRED_INTEGER_DIGITS); + test_flag!(required_fraction_digits, REQUIRED_FRACTION_DIGITS); + test_flag!(required_exponent_digits, REQUIRED_EXPONENT_DIGITS); + test_flag!(required_mantissa_digits, REQUIRED_MANTISSA_DIGITS); + test_flag!(no_positive_mantissa_sign, NO_POSITIVE_MANTISSA_SIGN); + test_flag!(required_mantissa_sign, REQUIRED_MANTISSA_SIGN); + test_flag!(no_exponent_notation, NO_EXPONENT_NOTATION); + test_flag!(no_positive_exponent_sign, NO_POSITIVE_EXPONENT_SIGN); + test_flag!(required_exponent_sign, REQUIRED_EXPONENT_SIGN); + test_flag!(no_exponent_without_fraction, NO_EXPONENT_WITHOUT_FRACTION); + test_flag!(no_special, NO_SPECIAL); + test_flag!(case_sensitive_special, CASE_SENSITIVE_SPECIAL); + test_flag!(no_integer_leading_zeros, NO_INTEGER_LEADING_ZEROS); + test_flag!(no_float_leading_zeros, NO_FLOAT_LEADING_ZEROS); + test_flag!(required_exponent_notation, REQUIRED_EXPONENT_NOTATION); + test_flag!(case_sensitive_exponent, CASE_SENSITIVE_EXPONENT); + #[cfg(feature = "power-of-two")] + test_flag!(case_sensitive_base_prefix, CASE_SENSITIVE_BASE_PREFIX); + #[cfg(feature = "power-of-two")] + test_flag!(case_sensitive_base_suffix, CASE_SENSITIVE_BASE_SUFFIX); + test_flag!(integer_internal_digit_separator, INTEGER_INTERNAL_DIGIT_SEPARATOR); + test_flag!(fraction_internal_digit_separator, FRACTION_INTERNAL_DIGIT_SEPARATOR); + test_flag!(exponent_internal_digit_separator, EXPONENT_INTERNAL_DIGIT_SEPARATOR); + test_flag!(integer_leading_digit_separator, INTEGER_LEADING_DIGIT_SEPARATOR); + test_flag!(fraction_leading_digit_separator, FRACTION_LEADING_DIGIT_SEPARATOR); + test_flag!(exponent_leading_digit_separator, EXPONENT_LEADING_DIGIT_SEPARATOR); + test_flag!(integer_trailing_digit_separator, INTEGER_TRAILING_DIGIT_SEPARATOR); + test_flag!(fraction_trailing_digit_separator, FRACTION_TRAILING_DIGIT_SEPARATOR); + test_flag!(exponent_trailing_digit_separator, EXPONENT_TRAILING_DIGIT_SEPARATOR); + test_flag!(integer_consecutive_digit_separator, INTEGER_CONSECUTIVE_DIGIT_SEPARATOR); + test_flag!(fraction_consecutive_digit_separator, FRACTION_CONSECUTIVE_DIGIT_SEPARATOR); + test_flag!(exponent_consecutive_digit_separator, EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR); + test_flag!(special_digit_separator, SPECIAL_DIGIT_SEPARATOR); +} + +#[test] +fn constants_test() { + // Don't check the actual values: just check they're defined. + let _: u128 = format::RUST_LITERAL; + let _: u128 = format::RUST_STRING; + let _: u128 = format::PYTHON_LITERAL; + let _: u128 = format::PYTHON_STRING; + let _: u128 = format::PYTHON3_LITERAL; + let _: u128 = format::PYTHON3_STRING; + let _: u128 = format::PYTHON36_LITERAL; + let _: u128 = format::PYTHON35_LITERAL; + let _: u128 = format::PYTHON2_LITERAL; + let _: u128 = format::PYTHON2_STRING; + let _: u128 = format::CXX17_LITERAL; + let _: u128 = format::CXX17_STRING; + let _: u128 = format::CXX14_LITERAL; + let _: u128 = format::CXX14_STRING; + let _: u128 = format::CXX11_LITERAL; + let _: u128 = format::CXX11_STRING; + let _: u128 = format::CXX03_LITERAL; + let _: u128 = format::CXX03_STRING; + let _: u128 = format::CXX98_LITERAL; + let _: u128 = format::CXX98_STRING; + let _: u128 = format::C18_LITERAL; + let _: u128 = format::C18_STRING; + let _: u128 = format::C11_LITERAL; + let _: u128 = format::C11_STRING; + let _: u128 = format::C99_LITERAL; + let _: u128 = format::C99_STRING; + let _: u128 = format::C90_LITERAL; + let _: u128 = format::C90_STRING; + let _: u128 = format::C89_LITERAL; + let _: u128 = format::C89_STRING; + let _: u128 = format::RUBY_LITERAL; + let _: u128 = format::RUBY_STRING; + let _: u128 = format::SWIFT_LITERAL; + let _: u128 = format::SWIFT_STRING; + let _: u128 = format::GO_LITERAL; + let _: u128 = format::GO_STRING; + let _: u128 = format::HASKELL_LITERAL; + let _: u128 = format::HASKELL_STRING; + let _: u128 = format::JAVASCRIPT_LITERAL; + let _: u128 = format::JAVASCRIPT_STRING; + let _: u128 = format::PERL_LITERAL; + let _: u128 = format::PERL_STRING; + let _: u128 = format::PHP_LITERAL; + let _: u128 = format::PHP_STRING; + let _: u128 = format::JAVA_LITERAL; + let _: u128 = format::JAVA_STRING; + let _: u128 = format::R_LITERAL; + let _: u128 = format::R_STRING; + let _: u128 = format::KOTLIN_LITERAL; + let _: u128 = format::KOTLIN_STRING; + let _: u128 = format::JULIA_LITERAL; + let _: u128 = format::JULIA_STRING; + let _: u128 = format::CSHARP7_LITERAL; + let _: u128 = format::CSHARP7_STRING; + let _: u128 = format::CSHARP6_LITERAL; + let _: u128 = format::CSHARP6_STRING; + let _: u128 = format::CSHARP5_LITERAL; + let _: u128 = format::CSHARP5_STRING; + let _: u128 = format::CSHARP4_LITERAL; + let _: u128 = format::CSHARP4_STRING; + let _: u128 = format::CSHARP3_LITERAL; + let _: u128 = format::CSHARP3_STRING; + let _: u128 = format::CSHARP2_LITERAL; + let _: u128 = format::CSHARP2_STRING; + let _: u128 = format::CSHARP1_LITERAL; + let _: u128 = format::CSHARP1_STRING; + let _: u128 = format::KAWA_LITERAL; + let _: u128 = format::KAWA_STRING; + let _: u128 = format::GAMBITC_LITERAL; + let _: u128 = format::GAMBITC_STRING; + let _: u128 = format::GUILE_LITERAL; + let _: u128 = format::GUILE_STRING; + let _: u128 = format::CLOJURE_LITERAL; + let _: u128 = format::CLOJURE_STRING; + let _: u128 = format::ERLANG_LITERAL; + let _: u128 = format::ERLANG_STRING; + let _: u128 = format::ELM_LITERAL; + let _: u128 = format::ELM_STRING; + let _: u128 = format::SCALA_LITERAL; + let _: u128 = format::SCALA_STRING; + let _: u128 = format::ELIXIR_LITERAL; + let _: u128 = format::ELIXIR_STRING; + let _: u128 = format::FORTRAN_LITERAL; + let _: u128 = format::FORTRAN_STRING; + let _: u128 = format::D_LITERAL; + let _: u128 = format::D_STRING; + let _: u128 = format::COFFEESCRIPT_LITERAL; + let _: u128 = format::COFFEESCRIPT_STRING; + let _: u128 = format::COBOL_LITERAL; + let _: u128 = format::COBOL_STRING; + let _: u128 = format::FSHARP_LITERAL; + let _: u128 = format::FSHARP_STRING; + let _: u128 = format::VB_LITERAL; + let _: u128 = format::VB_STRING; + let _: u128 = format::OCAML_LITERAL; + let _: u128 = format::OCAML_STRING; + let _: u128 = format::OBJECTIVEC_LITERAL; + let _: u128 = format::OBJECTIVEC_STRING; + let _: u128 = format::REASONML_LITERAL; + let _: u128 = format::REASONML_STRING; + let _: u128 = format::OCTAVE_LITERAL; + let _: u128 = format::OCTAVE_STRING; + let _: u128 = format::MATLAB_LITERAL; + let _: u128 = format::MATLAB_STRING; + let _: u128 = format::ZIG_LITERAL; + let _: u128 = format::ZIG_STRING; + let _: u128 = format::SAGE_LITERAL; + let _: u128 = format::SAGE_STRING; + let _: u128 = format::JSON; + let _: u128 = format::TOML; + let _: u128 = format::YAML; + let _: u128 = format::XML; + let _: u128 = format::SQLITE; + let _: u128 = format::POSTGRESQL; + let _: u128 = format::MYSQL; + let _: u128 = format::MONGODB; +} diff --git a/rust/vendor/lexical-util/tests/format_builder_tests.rs b/rust/vendor/lexical-util/tests/format_builder_tests.rs new file mode 100644 index 0000000..80b6c09 --- /dev/null +++ b/rust/vendor/lexical-util/tests/format_builder_tests.rs @@ -0,0 +1,60 @@ +use lexical_util::format::{NumberFormat, NumberFormatBuilder}; + +#[test] +fn decimal_test() { + const FORMAT: u128 = NumberFormatBuilder::decimal(); + let format = NumberFormat:: {}; + assert!(format.is_valid()); + assert_eq!(format.radix(), 10); + assert_eq!(format.mantissa_radix(), 10); + assert_eq!(format.exponent_base(), 10); + assert_eq!(format.exponent_radix(), 10); +} + +#[test] +#[cfg(feature = "power-of-two")] +fn binary_test() { + const FORMAT: u128 = NumberFormatBuilder::binary(); + let format = NumberFormat:: {}; + assert!(format.is_valid()); + assert_eq!(format.radix(), 2); + assert_eq!(format.mantissa_radix(), 2); + assert_eq!(format.exponent_base(), 2); + assert_eq!(format.exponent_radix(), 2); +} + +#[test] +#[cfg(feature = "power-of-two")] +fn octal_test() { + const FORMAT: u128 = NumberFormatBuilder::octal(); + let format = NumberFormat:: {}; + assert!(format.is_valid()); + assert_eq!(format.radix(), 8); + assert_eq!(format.mantissa_radix(), 8); + assert_eq!(format.exponent_base(), 8); + assert_eq!(format.exponent_radix(), 8); +} + +#[test] +#[cfg(feature = "power-of-two")] +fn hexadecimal_test() { + const FORMAT: u128 = NumberFormatBuilder::hexadecimal(); + let format = NumberFormat:: {}; + assert!(format.is_valid()); + assert_eq!(format.radix(), 16); + assert_eq!(format.mantissa_radix(), 16); + assert_eq!(format.exponent_base(), 16); + assert_eq!(format.exponent_radix(), 16); +} + +#[test] +#[cfg(feature = "power-of-two")] +fn from_radix_test() { + const FORMAT: u128 = NumberFormatBuilder::from_radix(32); + let format = NumberFormat:: {}; + assert!(format.is_valid()); + assert_eq!(format.radix(), 32); + assert_eq!(format.mantissa_radix(), 32); + assert_eq!(format.exponent_base(), 32); + assert_eq!(format.exponent_radix(), 32); +} diff --git a/rust/vendor/lexical-util/tests/format_flags_tests.rs b/rust/vendor/lexical-util/tests/format_flags_tests.rs new file mode 100644 index 0000000..039f81c --- /dev/null +++ b/rust/vendor/lexical-util/tests/format_flags_tests.rs @@ -0,0 +1,60 @@ +#[cfg(feature = "format")] +use core::num; + +#[cfg(feature = "format")] +use lexical_util::format; + +#[cfg(feature = "format")] +const fn from_digit_separator(digit_separator: u8) -> u128 { + format::NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(digit_separator)) + .digit_separator_flags(true) + .build_unchecked() +} + +#[cfg(feature = "format")] +fn is_valid_digit_separator(digit_separator: u8) -> bool { + let format = from_digit_separator(digit_separator); + format::is_valid_digit_separator(format) +} + +#[test] +#[cfg(feature = "format")] +fn test_is_valid_digit_separator() { + assert_eq!(is_valid_digit_separator(b'_'), true); + assert_eq!(is_valid_digit_separator(b'\''), true); + assert_eq!(is_valid_digit_separator(b'.'), true); + assert_eq!(is_valid_digit_separator(b'e'), true); + assert_eq!(is_valid_digit_separator(b'0'), false); + assert_eq!(is_valid_digit_separator(128), false); + + // Try with a custom radix. + #[cfg(feature = "radix")] + { + let format = format::NumberFormat::<{ from_digit_separator(b'e') }>::rebuild() + .radix(16) + .build_unchecked(); + assert_eq!(format::is_valid_digit_separator(format), false); + } +} + +#[cfg(all(feature = "power-of-two", feature = "format"))] +fn is_valid_punctuation(digit_separator: u8, base_prefix: u8, base_suffix: u8) -> bool { + let fmt = format::NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(digit_separator)) + .digit_separator_flags(true) + .base_prefix(num::NonZeroU8::new(base_prefix)) + .base_suffix(num::NonZeroU8::new(base_suffix)) + .build_unchecked(); + format::is_valid_punctuation(fmt) +} + +#[test] +#[cfg(all(feature = "power-of-two", feature = "format"))] +fn test_is_valid_punctuation() { + assert_eq!(is_valid_punctuation(b'_', b'h', 0), true); + assert_eq!(is_valid_punctuation(b'e', b'h', 0), true); + assert_eq!(is_valid_punctuation(b'^', b'h', 0), true); + assert_eq!(is_valid_punctuation(b'\'', b'h', 0), true); + assert_eq!(is_valid_punctuation(b'\'', b'h', b'h'), false); +} diff --git a/rust/vendor/lexical-util/tests/iterator_tests.rs b/rust/vendor/lexical-util/tests/iterator_tests.rs new file mode 100644 index 0000000..9550a78 --- /dev/null +++ b/rust/vendor/lexical-util/tests/iterator_tests.rs @@ -0,0 +1,115 @@ +#![cfg(any(feature = "parse-floats", feature = "parse-integers"))] + +use lexical_util::iterator::{AsBytes, Bytes, DigitsIter, Iter}; + +#[test] +#[cfg(not(feature = "format"))] +fn digits_iterator_test() { + use lexical_util::format::STANDARD; + + type Digits<'a> = Bytes<'a, { STANDARD }>; + assert!(Digits::IS_CONTIGUOUS); + + let digits = b"12345"; + let mut byte1 = Digits::new(digits); + let mut byte2 = Digits::new(digits); + assert!(byte1.integer_iter().eq(byte2.integer_iter())); + + let mut byte = digits.bytes::<{ STANDARD }>(); + let mut iter = byte.integer_iter(); + assert_eq!(iter.as_slice(), &digits[..]); + assert_eq!(iter.as_ptr(), digits.as_ptr()); + assert_eq!(iter.is_consumed(), false); + assert_eq!(iter.is_buffer_empty(), false); + assert_eq!(u32::from_le(iter.peek_u32().unwrap()), 0x34333231); + assert_eq!(iter.buffer_length(), 5); + assert_eq!(iter.cursor(), 0); + assert_eq!(iter.current_count(), 0); + unsafe { + iter.step_by_unchecked(4); + } + assert_eq!(iter.buffer_length(), 5); + assert_eq!(iter.cursor(), 4); + assert_eq!(iter.current_count(), 4); + assert_eq!(iter.peek(), Some(&b'5')); + assert_eq!(iter.peek(), Some(&b'5')); + assert_eq!(iter.next(), Some(&b'5')); + assert_eq!(iter.peek(), None); + assert_eq!(iter.next(), None); + + let mut byte = digits.bytes::<{ STANDARD }>(); + let mut iter = byte.integer_iter(); + assert_eq!(iter.peek_u64(), None); + assert_eq!(iter.nth(4).unwrap(), &b'5'); + assert_eq!(iter.as_slice(), &digits[digits.len()..]); + assert_eq!(iter.as_ptr(), digits[digits.len()..].as_ptr()); + + let mut byte = digits.bytes::<{ STANDARD }>(); + let mut iter = byte.integer_iter(); + assert_eq!(iter.peek(), Some(&b'1')); + unsafe { + iter.step_unchecked(); + } + assert_eq!(iter.peek(), Some(&b'2')); + unsafe { + iter.step_unchecked(); + } +} + +#[test] +#[cfg(feature = "format")] +fn skip_iterator_test() { + use core::num; + + use lexical_util::format::NumberFormatBuilder; + + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .digit_separator_flags(true) + .build_strict(); + + type Digits<'a> = Bytes<'a, { FORMAT }>; + assert!(!Digits::IS_CONTIGUOUS); + + let digits = b"123_45"; + let mut byte1 = Digits::new(digits); + let mut byte2 = Digits::new(digits); + assert!(byte1.integer_iter().eq(byte2.integer_iter())); + + let mut byte = digits.bytes::<{ FORMAT }>(); + let mut iter = byte.integer_iter(); + assert_eq!(iter.as_slice(), &digits[..]); + assert_eq!(iter.as_ptr(), digits.as_ptr()); + assert_eq!(iter.is_consumed(), false); + assert_eq!(iter.is_buffer_empty(), false); + assert_eq!(iter.buffer_length(), 6); + assert_eq!(iter.cursor(), 0); + assert_eq!(iter.current_count(), 0); + unsafe { iter.step_unchecked() }; + assert_eq!(iter.cursor(), 1); + assert_eq!(iter.current_count(), 0); + iter.next(); + assert_eq!(iter.cursor(), 2); + assert_eq!(iter.current_count(), 1); + + let mut byte = digits.bytes::<{ FORMAT }>(); + let mut iter = byte.integer_iter(); + assert_eq!(iter.peek(), Some(&b'1')); + assert_eq!(iter.peek(), Some(&b'1')); + assert_eq!(iter.next(), Some(&b'1')); + assert_eq!(iter.next(), Some(&b'2')); + assert_eq!(iter.next(), Some(&b'3')); + assert_eq!(iter.cursor(), 3); + assert_eq!(iter.current_count(), 3); + assert_eq!(iter.next(), Some(&b'4')); + assert_eq!(iter.cursor(), 5); + assert_eq!(iter.current_count(), 4); + assert_eq!(iter.next(), Some(&b'5')); + assert_eq!(iter.next(), None); + + let mut byte = digits.bytes::<{ FORMAT }>(); + let mut iter = byte.integer_iter(); + assert_eq!(iter.nth(4).unwrap(), &b'5'); + assert_eq!(iter.as_slice(), &digits[digits.len()..]); + assert_eq!(iter.as_ptr(), digits[digits.len()..].as_ptr()); +} diff --git a/rust/vendor/lexical-util/tests/not_feature_format_tests.rs b/rust/vendor/lexical-util/tests/not_feature_format_tests.rs new file mode 100644 index 0000000..50daccc --- /dev/null +++ b/rust/vendor/lexical-util/tests/not_feature_format_tests.rs @@ -0,0 +1,53 @@ +#![cfg(not(feature = "format"))] + +use lexical_util::format::{self, NumberFormat, STANDARD}; + +#[test] +fn format_properties_test() { + let format = NumberFormat::<{ STANDARD }> {}; + assert_eq!(format.flags(), STANDARD & format::FLAG_MASK); + assert_eq!(format.interface_flags(), STANDARD & format::INTERFACE_FLAG_MASK); + assert_eq!(format.digit_separator(), b'\x00'); + assert_eq!(format.base_prefix(), b'\x00'); + assert_eq!(format.base_suffix(), b'\x00'); + assert_eq!(format.mantissa_radix(), 10); + assert_eq!(format.radix(), 10); + assert_eq!(format.exponent_base(), 10); + assert_eq!(format.exponent_radix(), 10); + assert_eq!(format.required_integer_digits(), false); + assert_eq!(format.required_fraction_digits(), false); + assert_eq!(format.required_exponent_digits(), true); + assert_eq!(format.required_mantissa_digits(), true); + assert_eq!(format.required_digits(), true); + assert_eq!(format.no_positive_mantissa_sign(), false); + assert_eq!(format.required_mantissa_sign(), false); + assert_eq!(format.no_exponent_notation(), false); + assert_eq!(format.no_positive_exponent_sign(), false); + assert_eq!(format.required_exponent_sign(), false); + assert_eq!(format.no_exponent_without_fraction(), false); + assert_eq!(format.no_special(), false); + assert_eq!(format.case_sensitive_special(), false); + assert_eq!(format.no_integer_leading_zeros(), false); + assert_eq!(format.no_float_leading_zeros(), false); + assert_eq!(format.required_exponent_notation(), false); + assert_eq!(format.case_sensitive_exponent(), false); + assert_eq!(format.case_sensitive_base_prefix(), false); + assert_eq!(format.case_sensitive_base_suffix(), false); + assert_eq!(format.integer_internal_digit_separator(), false); + assert_eq!(format.fraction_internal_digit_separator(), false); + assert_eq!(format.exponent_internal_digit_separator(), false); + assert_eq!(format.internal_digit_separator(), false); + assert_eq!(format.integer_leading_digit_separator(), false); + assert_eq!(format.fraction_leading_digit_separator(), false); + assert_eq!(format.exponent_leading_digit_separator(), false); + assert_eq!(format.leading_digit_separator(), false); + assert_eq!(format.integer_trailing_digit_separator(), false); + assert_eq!(format.fraction_trailing_digit_separator(), false); + assert_eq!(format.exponent_trailing_digit_separator(), false); + assert_eq!(format.trailing_digit_separator(), false); + assert_eq!(format.integer_consecutive_digit_separator(), false); + assert_eq!(format.fraction_consecutive_digit_separator(), false); + assert_eq!(format.exponent_consecutive_digit_separator(), false); + assert_eq!(format.consecutive_digit_separator(), false); + assert_eq!(format.special_digit_separator(), false); +} diff --git a/rust/vendor/lexical-util/tests/num_tests.rs b/rust/vendor/lexical-util/tests/num_tests.rs new file mode 100644 index 0000000..8849deb --- /dev/null +++ b/rust/vendor/lexical-util/tests/num_tests.rs @@ -0,0 +1,254 @@ +use lexical_util::num; + +fn as_primitive(t: T) { + let _: u8 = t.as_u8(); + let _: u16 = t.as_u16(); + let _: u32 = t.as_u32(); + let _: u64 = t.as_u64(); + let _: u128 = t.as_u128(); + let _: usize = t.as_usize(); + let _: i8 = t.as_i8(); + let _: i16 = t.as_i16(); + let _: i32 = t.as_i32(); + let _: i64 = t.as_i64(); + let _: i128 = t.as_i128(); + let _: isize = t.as_isize(); + let _: f32 = t.as_f32(); + let _: f64 = t.as_f64(); +} + +#[test] +fn as_primitive_test() { + as_primitive(1u8); + as_primitive(1u16); + as_primitive(1u32); + as_primitive(1u64); + as_primitive(1u128); + as_primitive(1usize); + as_primitive(1i8); + as_primitive(1i16); + as_primitive(1i32); + as_primitive(1i64); + as_primitive(1i128); + as_primitive(1isize); + as_primitive(1f32); + as_primitive(1f64); +} + +fn as_cast(t: T) { + let _: i8 = num::as_cast(t); + let _: i16 = num::as_cast(t); + let _: i32 = num::as_cast(t); + let _: i64 = num::as_cast(t); + let _: i128 = num::as_cast(t); + let _: isize = num::as_cast(t); + let _: u8 = num::as_cast(t); + let _: u16 = num::as_cast(t); + let _: u32 = num::as_cast(t); + let _: u64 = num::as_cast(t); + let _: u128 = num::as_cast(t); + let _: usize = num::as_cast(t); + let _: f32 = num::as_cast(t); + let _: f64 = num::as_cast(t); +} + +#[test] +fn as_cast_test() { + as_cast(1u8); + as_cast(1u16); + as_cast(1u32); + as_cast(1u64); + as_cast(1u128); + as_cast(1usize); + as_cast(1i8); + as_cast(1i16); + as_cast(1i32); + as_cast(1i64); + as_cast(1i128); + as_cast(1isize); + as_cast(1f32); + as_cast(1f64); +} + +fn check_number(x: T, mut y: T) { + // Copy, partialeq, partialord + let _ = x; + assert!(x < y); + assert!(x != y); + + // Operations + let _ = y + x; + let _ = y - x; + let _ = y * x; + let _ = y / x; + let _ = y % x; + y += x; + y -= x; + y *= x; + y /= x; + y %= x; + + // Conversions already tested. +} + +#[test] +fn number_test() { + check_number(1u8, 5); + check_number(1u16, 5); + check_number(1u32, 5); + check_number(1u64, 5); + check_number(1u128, 5); + check_number(1usize, 5); + check_number(1i8, 5); + check_number(1i16, 5); + check_number(1i32, 5); + check_number(1i64, 5); + check_number(1i128, 5); + check_number(1isize, 5); + check_number(1f32, 5.0); + check_number(1f64, 5.0); +} + +fn check_integer(mut x: T) { + // Copy, partialeq, partialord, ord, eq + let _ = x; + assert!(x > T::ONE); + assert!(x != T::ONE); + assert_eq!(x.min(T::ONE), T::ONE); + assert_eq!(x.max(T::ONE), x); + + // Operations + let _ = x + T::ONE; + let _ = x - T::ONE; + let _ = x * T::ONE; + let _ = x / T::ONE; + let _ = x % T::ONE; + x += T::ONE; + x -= T::ONE; + x *= T::ONE; + x /= T::ONE; + x %= T::ONE; + + // Bitwise operations + let _ = x & T::ONE; + let _ = x | T::ONE; + let _ = x ^ T::ONE; + x &= T::ONE; + x |= T::ONE; + x ^= T::ONE; + + // Bit shifts + let _ = x << 1i32; + let _ = x >> 1i32; + x <<= 1i32; + x >>= 1i32; + + // Conversions already tested. +} + +#[test] +fn integer_test() { + check_integer(65u8); + check_integer(65u16); + check_integer(65u32); + check_integer(65u64); + check_integer(65u128); + check_integer(65usize); + check_integer(65i8); + check_integer(65i16); + check_integer(65i32); + check_integer(65i64); + check_integer(65i128); + check_integer(65isize); +} + +#[test] +fn ceil_divmod_test() { + use lexical_util::num::Integer; + + assert_eq!(5usize.ceil_divmod(7), (1, -2)); + assert_eq!(0usize.ceil_divmod(7), (0, 0)); + assert_eq!(35usize.ceil_divmod(7), (5, 0)); + assert_eq!(36usize.ceil_divmod(7), (6, -6)); +} + +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] +fn check_float(mut x: T) { + // Copy, partialeq, partialord + let _ = x; + assert!(x > T::ONE); + assert!(x != T::ONE); + + // Operations + let _ = x + T::ONE; + let _ = x - T::ONE; + let _ = x * T::ONE; + let _ = x / T::ONE; + let _ = x % T::ONE; + let _ = -x; + x += T::ONE; + x -= T::ONE; + x *= T::ONE; + x /= T::ONE; + x %= T::ONE; + + // Check functions + let _ = x.to_bits(); + assert_eq!(T::from_bits(x.to_bits()), x); + let _ = x.is_sign_positive(); + let _ = x.is_sign_negative(); + let _ = x.ln(); + let _ = x.floor(); + + // Check properties + let _ = x.to_bits() & T::SIGN_MASK; + let _ = x.to_bits() & T::EXPONENT_MASK; + let _ = x.to_bits() & T::HIDDEN_BIT_MASK; + let _ = x.to_bits() & T::MANTISSA_MASK; + assert!(T::from_bits(T::INFINITY_BITS).is_special()); +} + +#[test] +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] +fn float_test() { + use lexical_util::num::Float; + + check_float(123f32); + check_float(123f64); + + // b00000000000000000000000000000001 + let f: f32 = 1e-45; + assert!(f.is_odd()); + assert!(f.next().is_even()); + assert!(f.next_positive().is_even()); + assert!(f.prev().is_even()); + assert!(f.prev_positive().is_even()); + assert!(f.round_positive_even().is_even()); + assert_eq!(f.prev().next(), f); + assert_eq!(f.prev_positive().next_positive(), f); + assert_eq!(f.round_positive_even(), f.next()); + + // b00111101110011001100110011001101 + let f: f32 = 0.1; + assert!(f.is_odd()); + assert!(f.next().is_even()); + assert!(f.next_positive().is_even()); + assert!(f.prev().is_even()); + assert!(f.prev_positive().is_even()); + assert!(f.round_positive_even().is_even()); + assert_eq!(f.prev().next(), f); + assert_eq!(f.prev_positive().next_positive(), f); + assert_eq!(f.round_positive_even(), f.next()); + + // b01000000000000000000000000000000 + let f: f32 = 1.0; + assert!(f.is_even()); + assert!(f.next().is_odd()); + assert!(f.next_positive().is_odd()); + assert!(f.prev().is_odd()); + assert!(f.prev_positive().is_odd()); + assert!(f.round_positive_even().is_even()); + assert_eq!(f.prev().next(), f); + assert_eq!(f.prev_positive().next_positive(), f); + assert_ne!(f.round_positive_even(), f.next()); +} diff --git a/rust/vendor/lexical-util/tests/skip_tests.rs b/rust/vendor/lexical-util/tests/skip_tests.rs new file mode 100644 index 0000000..9955daa --- /dev/null +++ b/rust/vendor/lexical-util/tests/skip_tests.rs @@ -0,0 +1,590 @@ +#![cfg(all(feature = "format", any(feature = "parse-floats", feature = "parse-integers")))] + +use core::num; + +use lexical_util::format::NumberFormatBuilder; +use lexical_util::iterator::AsBytes; + +fn skip_iter_eq(input: &[u8], output: &[u8]) { + // next is done in terms of peek, so we're safe here. + let mut input = input.bytes::<{ FORMAT }>(); + let mut output = output.bytes::<{ FORMAT }>(); + assert!(input.integer_iter().eq(output.integer_iter())); +} + +#[test] +fn test_skip_iter_i() { + // Test iterators that skip single, internal-only digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_internal_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b"_.45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b"__.45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4_"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4__"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4_."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4__."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"_455"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"__45__5"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b"_.455"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b"__.45__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"45_"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"4__5__"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"45_.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"4__5__.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"_45_"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"__45__"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"_45_.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"__45__.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"_45_"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"__4__5__"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"_45_.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"__4__5__.56"); +} + +#[test] +fn test_skip_iter_l() { + // Test iterators that skip single, leading-only digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_leading_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b"__.45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4_"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4__"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4_."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4__."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"45_5"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"__45__5"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".45_5"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b"__.45__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"4_5_"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"4__5__"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"4_5_.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"4__5__.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"45_"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"__45__"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"45_.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"__45__.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"4_5_"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"__4__5__"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"4_5_.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"__4__5__.56"); +} + +#[test] +fn test_skip_iter_t() { + // Test iterators that skip single, trailing-only digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_trailing_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b"__.45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4__"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4__."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"_45_5"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"__45__5"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".45_5"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b"__.45__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"4__5__"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"4_5.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"4__5__.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"__45__"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"_45.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"__45__.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"_4_5"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"__4__5__"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"_4_5.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"__4__5__.56"); +} + +#[test] +fn test_skip_iter_il() { + // Test iterators that skip single, internal or leading-only digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_internal_digit_separator(true) + .integer_leading_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b"__.45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4_"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4__"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4_."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4__."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"455"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"__45__5"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".455"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b"__.45__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"45_"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"4__5__"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"45_.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"4__5__.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"45_"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"__45__"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"45_.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"__45__.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"45_"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"__4__5__"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"45_.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"__4__5__.56"); +} + +#[test] +fn test_skip_iter_it() { + // Test iterators that skip single, internal or trailing-only digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_internal_digit_separator(true) + .integer_trailing_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b"__.45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4__"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4__."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"_455"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"__45__5"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".455"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b"__.45__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"4__5__"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"45.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"4__5__.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"__45__"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"_45.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"__45__.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"__4__5__"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"_45.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"__4__5__.56"); +} + +#[test] +fn test_skip_iter_lt() { + // Test iterators that skip single, leading or trailing-only digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_leading_digit_separator(true) + .integer_trailing_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b"__.45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4__"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4__."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"45_5"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"__45__5"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".45_5"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b"__.45__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"4__5__"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"4_5.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"4__5__.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"__45__"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"45.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"__45__.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"__4__5__"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"4_5.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"__4__5__.56"); +} + +#[test] +fn test_skip_iter_ilt() { + // Test iterators that skip single digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_internal_digit_separator(true) + .integer_leading_digit_separator(true) + .integer_trailing_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b"__.45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4__"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4__."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"455"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"__45__5"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".455"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b"__.45__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"4__5__"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"45.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"4__5__.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"__45__"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"45.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"__45__.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"__4__5__"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"45.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"__4__5__.56"); +} + +#[test] +fn test_skip_iter_ic() { + // Test iterators that skip multiple, internal digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_internal_digit_separator(true) + .integer_consecutive_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b"_.45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b"__.45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4_"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4__"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4_."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4__."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"_455"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"__455"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b"_.455"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b"__.455"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"45_"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"45__"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"45_.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"45__.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"_45_"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"__45__"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"_45_.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"__45__.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"_45_"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"__45__"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"_45_.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"__45__.56"); +} + +#[test] +fn test_skip_iter_lc() { + // Test iterators that skip multiple, leading digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_leading_digit_separator(true) + .integer_consecutive_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4_"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4__"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4_."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4__."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"45_5"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"45__5"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".45_5"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b".45__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"4_5_"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"4__5__"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"4_5_.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"4__5__.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"45_"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"45__"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"45_.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"45__.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"4_5_"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"4__5__"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"4_5_.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"4__5__.56"); +} + +#[test] +fn test_skip_iter_tc() { + // Test iterators that skip multiple, trailing digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_trailing_digit_separator(true) + .integer_consecutive_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"_45_5"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"__45__5"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".45_5"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b".45__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"4_5.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"4__5.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"_45.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"__45.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"_4_5"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"__4__5"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"_4_5.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"__4__5.56"); +} + +#[test] +fn test_skip_iter_ilc() { + // Test iterators that skip multiple, internal or leading digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_internal_digit_separator(true) + .integer_leading_digit_separator(true) + .integer_consecutive_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4_"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4__"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4_."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4__."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"455"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"455"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".455"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b".455"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"45_"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"45__"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"45_.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"45__.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"45_"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"45__"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"45_.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"45__.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"45_"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"45__"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"45_.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"45__.56"); +} + +#[test] +fn test_skip_iter_itc() { + // Test iterators that skip multiple, internal or trailing digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_internal_digit_separator(true) + .integer_trailing_digit_separator(true) + .integer_consecutive_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"_455"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"__455"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".455"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b".455"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"45.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"45.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"_45.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"__45.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"_45"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"__45"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"_45.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"__45.56"); +} + +#[test] +fn test_skip_iter_ltc() { + // Test iterators that skip multiple, leading or trailing digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_leading_digit_separator(true) + .integer_trailing_digit_separator(true) + .integer_consecutive_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"45_5"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"45__5"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".45_5"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b".45__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"4_5.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"4__5.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"45"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"45.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"45.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"4_5"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"4__5"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"4_5.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"4__5.56"); +} + +#[test] +fn test_skip_iter_iltc() { + // Test iterators that skip multiple digit separators. + pub const FORMAT: u128 = NumberFormatBuilder::new() + .digit_separator(num::NonZeroU8::new(b'_')) + .integer_internal_digit_separator(true) + .integer_leading_digit_separator(true) + .integer_trailing_digit_separator(true) + .integer_consecutive_digit_separator(true) + .build_strict(); + + skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); + skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); + skip_iter_eq::<{ FORMAT }>(b"1e", b"1e"); + skip_iter_eq::<{ FORMAT }>(b"1", b"1"); + skip_iter_eq::<{ FORMAT }>(b"_45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45", b"45"); + skip_iter_eq::<{ FORMAT }>(b"_.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"__.45", b".45"); + skip_iter_eq::<{ FORMAT }>(b"4_5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4_", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4__", b"4"); + skip_iter_eq::<{ FORMAT }>(b"4_.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"4__.", b"4."); + skip_iter_eq::<{ FORMAT }>(b"_45_5", b"455"); + skip_iter_eq::<{ FORMAT }>(b"__45__5", b"455"); + skip_iter_eq::<{ FORMAT }>(b"_.45_5", b".455"); + skip_iter_eq::<{ FORMAT }>(b"__.45__5", b".455"); + skip_iter_eq::<{ FORMAT }>(b"4_5_", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4__5__", b"45"); + skip_iter_eq::<{ FORMAT }>(b"4_5_.5", b"45.5"); + skip_iter_eq::<{ FORMAT }>(b"4__5__.5", b"45.5"); + skip_iter_eq::<{ FORMAT }>(b"_45_", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__45__", b"45"); + skip_iter_eq::<{ FORMAT }>(b"_45_.56", b"45.56"); + skip_iter_eq::<{ FORMAT }>(b"__45__.56", b"45.56"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_", b"45"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__", b"45"); + skip_iter_eq::<{ FORMAT }>(b"_4_5_.56", b"45.56"); + skip_iter_eq::<{ FORMAT }>(b"__4__5__.56", b"45.56"); +} From 2e3f8f06e0d4cfa5ebd057c8fc2cd884b2cc90db Mon Sep 17 00:00:00 2001 From: Madhava Jay Date: Tue, 14 Apr 2026 17:36:31 +1000 Subject: [PATCH 2/2] ci: add linux lint+test workflow for bioscript submodule Runs lint.sh (cargo fmt + clippy -D warnings) and test.sh, plus the bioscript-formats integration tests which exercise the new CRAM streaming path and MD5 fallback against the checked-in fixtures. Submodules are fetched recursively so noodles is available as a patch. Also gitignores context/ (read-only htslib reference material kept locally during the noodles migration; not part of the build). --- .github/workflows/ci.yml | 37 +++++++++++++++++++++++++++++++++++++ .gitignore | 4 ++++ 2 files changed, 41 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9fc4448 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + lint-and-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Cache Rust build + uses: Swatinem/rust-cache@v2 + with: + workspaces: rust + + - name: Lint (cargo fmt + clippy) + run: ./lint.sh + + - name: Test (cargo test) + run: ./test.sh + + - name: Format integration tests (CRAM fixtures, MD5 fallback) + run: cargo test --manifest-path rust/Cargo.toml -p bioscript-formats --test file_formats -- --test-threads=1 diff --git a/.gitignore b/.gitignore index bd9ac83..88be062 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,7 @@ examples/**/work/* examples/**/.nextflow* # BioVault runtime config .biovault-runtime.config + +# Read-only reference material (htslib/rust-htslib source trees used during +# the CRAM/noodles migration; not part of the build). +context/