├── fuzz ├── .gitignore ├── fuzz_targets │ ├── fuzz_i128.rs │ ├── fuzz_i16.rs │ ├── fuzz_i32.rs │ ├── fuzz_i64.rs │ ├── fuzz_i8.rs │ ├── fuzz_u128.rs │ ├── fuzz_u16.rs │ ├── fuzz_u32.rs │ ├── fuzz_u64.rs │ ├── fuzz_u8.rs │ ├── fuzz_isize.rs │ └── fuzz_usize.rs └── Cargo.toml ├── .gitignore ├── .cargo └── config.toml ├── .github ├── renovate.json ├── dependabot.yml └── workflows │ ├── semgrep.yml │ ├── check-main.yml │ └── ci.yml ├── LICENSE-MIT ├── Cargo.toml ├── README.md ├── src ├── parse_u16.rs ├── trees.rs ├── parse_i32.rs ├── parse_u8.rs ├── parse_i16.rs ├── parse_i8.rs ├── parse.rs ├── parse_u32.rs └── lib.rs ├── LICENSE-APACHE └── benches └── bench.rs /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | corpus 4 | artifacts 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | src/lib.rs 3 | fuzz/Cargo.lock 4 | .gitignore 5 | tags 6 | Cargo.lock 7 | tags 8 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # rustflags = ["-C", "target-cpu=native"] 3 | 4 | [target.wasm32-unknown-unknown] 5 | runner = 'wasm-bindgen-test-runner' -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | "packageRules": [ 4 | { 5 | "matchManagers": ["cargo"], 6 | "groupName": "cargo dependencies" 7 | }, 8 | { 9 | "managers": ["github-actions"], 10 | "pinVersions": true 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/semgrep.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: {} 3 | pull_request: {} 4 | push: 5 | branches: 6 | - main 7 | - master 8 | paths: 9 | - .github/workflows/semgrep.yml 10 | schedule: 11 | # random HH:MM to avoid a load spike on GitHub Actions at 00:00 12 | - cron: 41 21 * * * 13 | name: Semgrep 14 | jobs: 15 | semgrep: 16 | name: semgrep/ci 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: read 20 | env: 21 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} 22 | container: 23 | image: semgrep/semgrep 24 | steps: 25 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 26 | - run: semgrep ci 27 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_i128.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_i16.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_i32.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_i64.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_i8.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_u128.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_u16.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_u32.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_u64.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_u8.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_isize.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_usize.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use std::fmt::Debug; 3 | use std::str::FromStr; 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | fuzz_parse::(data) 9 | }); 10 | 11 | 12 | fn fuzz_parse(data: &[u8]) 13 | where T: atoi_radix10::FromStrRadixHelper + Debug + FromStr 14 | { 15 | if let Ok(s) = String::from_utf8(data.to_vec()){ 16 | let spec: Result = s.parse(); 17 | //assert_eq!(spec.map_err(|e| e.kind().clone()), atoi_radix10::parse::(&data).map_err(|e| e.kind)); 18 | assert_eq!(spec.map_err(|_e| ()), atoi_radix10::parse::(&data).map_err(|_e| ())); 19 | } else { 20 | //just make sure doesn't panic: 21 | let _result = atoi_radix10::parse::(&data); 22 | } 23 | } -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /.github/workflows/check-main.yml: -------------------------------------------------------------------------------- 1 | name: PR – Ensure main green 2 | on: { pull_request: { types: [opened, synchronize, reopened, ready_for_review] } } 3 | 4 | permissions: 5 | contents: read 6 | checks: read 7 | statuses: read 8 | 9 | jobs: 10 | ensure-main-green: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #v8.0.0 14 | with: 15 | script: | 16 | const { owner, repo } = context.repo; 17 | const { data: { sha } } = await github.rest.repos.getCommit({ owner, repo, ref: 'main' }); 18 | const runs = await github.paginate(github.rest.checks.listForRef, { 19 | owner, repo, ref: sha, filter: 'latest', per_page: 100 20 | }); 21 | const failing = runs.filter(r => r.status === 'completed' && r.conclusion !== 'success'); 22 | if (failing.length) { 23 | core.setFailed('main not green:\n' + failing 24 | .map(r => `- ${r.name}: ${r.html_url || r.details_url}`) 25 | .join('\n')); 26 | } else { 27 | core.info('main green'); 28 | } 29 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "atoi_radix10" 3 | version = "0.0.1" 4 | authors = ["Giles Cope ", "Ivan Tham "] 5 | edition = "2021" 6 | description = "Parse strings as base 10 integers quickly (especially u64/u128)" 7 | license = "MIT OR Apache-2.0" 8 | keywords = ["parse", "atoi", "conversion", "integer"] 9 | categories = ["parsing"] 10 | repository = "https://github.com/gilescope/atoi_radix10" 11 | 12 | 13 | [features] 14 | default = [] 15 | simd = [] 16 | 17 | [dependencies] 18 | # log = "*" 19 | # env_logger = "*" 20 | # version="*", 21 | # safe_arch={ version="0.6", optional=true } 22 | #wide = { git="https://github.com/gilescope/wide.git", branch="i16x16", optional=true } 23 | #core_simd = { git="https://github.com/rust-lang/stdsimd.git", optional=true } 24 | 25 | [dev-dependencies] 26 | # Creating good test names: 27 | paste = "1.0" 28 | # Benchmarking: 29 | #criterion-cycles-per-byte = "0.1" 30 | # These are for creating test strings (in no_std) 31 | heapless = "0.9" 32 | numtoa = "0.3.0" 33 | # Wasm testing: 34 | wasm-bindgen-test = "0.3" 35 | # We need to enable this as well for wasm: 36 | getrandom = "0.3" 37 | rand = "0.9" 38 | 39 | [target.'cfg(target_arch = "wasm32")'.dependencies] 40 | getrandom = {version = "0.3", features = ["wasm_js"]} 41 | 42 | [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] 43 | criterion = {version="0.7", features=["html_reports"]} 44 | 45 | [[bench]] 46 | name = "bench" 47 | harness = false 48 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "atoi_radix10-fuzz" 4 | version = "0.0.0" 5 | authors = ["Automatically generated"] 6 | publish = false 7 | edition = "2018" 8 | 9 | [package.metadata] 10 | cargo-fuzz = true 11 | 12 | [dependencies] 13 | libfuzzer-sys = "0.4" 14 | 15 | [dependencies.atoi_radix10] 16 | path = ".." 17 | 18 | # Prevent this from interfering with workspaces 19 | [workspace] 20 | members = ["."] 21 | 22 | [[bin]] 23 | name = "fuzz_u128" 24 | path = "fuzz_targets/fuzz_u128.rs" 25 | test = false 26 | doc = false 27 | 28 | [[bin]] 29 | name = "fuzz_u64" 30 | path = "fuzz_targets/fuzz_u64.rs" 31 | test = false 32 | doc = false 33 | 34 | [[bin]] 35 | name = "fuzz_u32" 36 | path = "fuzz_targets/fuzz_u32.rs" 37 | test = false 38 | doc = false 39 | 40 | [[bin]] 41 | name = "fuzz_u16" 42 | path = "fuzz_targets/fuzz_u16.rs" 43 | test = false 44 | doc = false 45 | 46 | [[bin]] 47 | name = "fuzz_u8" 48 | path = "fuzz_targets/fuzz_u8.rs" 49 | test = false 50 | doc = false 51 | 52 | [[bin]] 53 | name = "fuzz_usize" 54 | path = "fuzz_targets/fuzz_usize.rs" 55 | test = false 56 | doc = false 57 | 58 | 59 | 60 | 61 | [[bin]] 62 | name = "fuzz_i128" 63 | path = "fuzz_targets/fuzz_i128.rs" 64 | test = false 65 | doc = false 66 | 67 | [[bin]] 68 | name = "fuzz_i64" 69 | path = "fuzz_targets/fuzz_i64.rs" 70 | test = false 71 | doc = false 72 | 73 | [[bin]] 74 | name = "fuzz_i32" 75 | path = "fuzz_targets/fuzz_i32.rs" 76 | test = false 77 | doc = false 78 | 79 | [[bin]] 80 | name = "fuzz_i16" 81 | path = "fuzz_targets/fuzz_i16.rs" 82 | test = false 83 | doc = false 84 | 85 | [[bin]] 86 | name = "fuzz_i8" 87 | path = "fuzz_targets/fuzz_i8.rs" 88 | test = false 89 | doc = false 90 | 91 | [[bin]] 92 | name = "fuzz_isize" 93 | path = "fuzz_targets/fuzz_isize.rs" 94 | test = false 95 | doc = false 96 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | name: Continuous integration 4 | permissions: 5 | contents: read 6 | 7 | jobs: 8 | ci: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 12 | 13 | - uses: actions-rs/toolchain@b2417cde72dcf67f306c0ae8e0828a81bf0b189f # v1.0.6 14 | with: 15 | profile: minimal 16 | toolchain: stable 17 | override: true 18 | components: rustfmt, clippy 19 | 20 | - run: | 21 | cargo fmt --all -- --check 22 | cargo build --all-targets --release 23 | cargo test --release 24 | cargo clippy -- -D warnings 25 | 26 | wasi: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 30 | 31 | - uses: actions-rs/toolchain@b2417cde72dcf67f306c0ae8e0828a81bf0b189f # v1.0.6 32 | with: 33 | profile: minimal 34 | toolchain: stable 35 | override: true 36 | target: wasm32-wasip1 37 | components: rustfmt 38 | 39 | - run: | 40 | cargo install wasm-pack 41 | RUSTFLAGS='--cfg getrandom_backend="wasm_js"' wasm-pack test --node 42 | wasm32: 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 46 | 47 | - uses: actions-rs/toolchain@b2417cde72dcf67f306c0ae8e0828a81bf0b189f # v1.0.6 48 | with: 49 | profile: minimal 50 | toolchain: stable 51 | override: true 52 | target: wasm32-unknown-unknown 53 | components: rustfmt 54 | 55 | - run: | 56 | cargo install wasm-pack 57 | RUSTFLAGS='--cfg getrandom_backend="wasm_js"' wasm-pack test --node 58 | miri: 59 | runs-on: ubuntu-latest 60 | env: 61 | RUSTFLAGS: "" 62 | steps: 63 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 64 | 65 | - uses: actions-rs/toolchain@b2417cde72dcf67f306c0ae8e0828a81bf0b189f # v1.0.6 66 | with: 67 | profile: minimal 68 | toolchain: "nightly" 69 | override: true 70 | components: miri 71 | 72 | - run: cargo miri test 73 | fuzz-unsigned: 74 | runs-on: ubuntu-latest 75 | 76 | steps: 77 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 78 | 79 | - uses: actions-rs/toolchain@b2417cde72dcf67f306c0ae8e0828a81bf0b189f # v1.0.6 80 | with: 81 | profile: minimal 82 | toolchain: nightly 83 | override: true 84 | 85 | - uses: actions-rs/cargo@b0651d9f4d4983fd4cc8e94d927bdc0bb5566667 #v1.0.1 86 | with: 87 | command: install 88 | args: -f cargo-fuzz 89 | - run: | 90 | cargo fuzz run fuzz_u8 -- -max_len=42 -max_total_time=10s 91 | cargo fuzz run fuzz_u16 -- -max_len=42 -max_total_time=10s 92 | cargo fuzz run fuzz_u32 -- -max_len=42 -max_total_time=10s 93 | cargo fuzz run fuzz_u64 -- -max_len=42 -max_total_time=10s 94 | cargo fuzz run fuzz_u128 -- -max_len=42 -max_total_time=10s 95 | cargo fuzz run fuzz_usize -- -max_len=42 -max_total_time=10s 96 | 97 | fuzz-sign: 98 | runs-on: ubuntu-latest 99 | 100 | steps: 101 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 102 | 103 | - uses: actions-rs/toolchain@b2417cde72dcf67f306c0ae8e0828a81bf0b189f # v1.0.6 104 | with: 105 | profile: minimal 106 | toolchain: nightly 107 | override: true 108 | 109 | - uses: actions-rs/cargo@b0651d9f4d4983fd4cc8e94d927bdc0bb5566667 #v1.0.1 110 | with: 111 | command: install 112 | args: -f cargo-fuzz 113 | - run: | 114 | cargo fuzz run fuzz_i8 -- -max_len=42 -max_total_time=10s 115 | cargo fuzz run fuzz_i16 -- -max_len=42 -max_total_time=10s 116 | cargo fuzz run fuzz_i32 -- -max_len=42 -max_total_time=10s 117 | cargo fuzz run fuzz_i64 -- -max_len=42 -max_total_time=10s 118 | cargo fuzz run fuzz_i128 -- -max_len=42 -max_total_time=10s 119 | cargo fuzz run fuzz_isize -- -max_len=42 -max_total_time=10s 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![CI](https://github.com/gilescope/parseint/actions/workflows/ci.yml/badge.svg) 3 | 4 | Faster Integer Parsing (rust port) 5 | ================================== 6 | 7 | Minimum rust version: 1.55 8 | 9 | This repository is the rust port of @KholdStare experimentation in 10 | https://kholdstare.github.io/technical/2020/05/26/faster-integer-parsing.html 11 | 12 | There is also a blog post on this in Rust Malaysia. 13 | https://github.com/rust-malaysia/rust-malaysia.github.io/blob/master/_posts/2020-07-12-faster-integer-parsing.md 14 | 15 | From discussions on reddit, it turns out someone else has stumbled on the exact 16 | same ideas before @KholdStare, Wojciech Muła. 17 | http://0x80.pl/articles/simd-parsing-int-sequences.html 18 | 19 | Notes: 20 | 21 | Spec: 22 | 23 | +/- 0000000000000 then digits. 24 | 25 | Goals: 26 | 27 | An exploration of the fastest way to parse numbers without reading memory that you don't own. (Once happy with the result we can try and de-unsafe as much as possible 28 | while keeping the performance.) 29 | 30 | We try to obey the rule of small numbers and make sure single digit numbers are especially fast, and in general all numbers will be parsed faster than std. 31 | 32 | Performance 33 | =========== 34 | 35 | If you have to parse u128 and i128 numbers this crate does any number in under 25ns 36 | (and if you target a specific cpu with avx then maybe all under 15ns-20ns). It is hands down many many times faster than std rust (especially i128) across all the numbers. 37 | 38 | For u8/i8 it's about the same as std. 39 | 40 | For u16, u32, u64 it's around 1/3 faster than std. 41 | 42 | Usage and Features 43 | ================== 44 | 45 | `nightly` and `simd` features for highest speed (and target your specific cpu). 46 | 47 | It's `no_std` by default and will parse from any `[u8]` slice. 48 | 49 | How this works 50 | ============== 51 | This is called SWAR: Simd within a register. 52 | 53 | Optimisations that did help 54 | =========================== 55 | 56 | * Using likely and unlikely intrinsics. 57 | * Moving `+` further up before it was accessed due to latency requirements. 58 | * Try not to do any instructions requiring latency just before returning. For example `if cond { return a as u16 }`, you can calculate the `a as u16` before the if then it's faster. (We're optimising for the happy path) 59 | * It was much easier to start fast and try and add things in than to start slow and make it faster (the latter you are in the dark as to why it is slow and just guessing, where as if you build it up you know instantly when you add something in that borks the speed.). 60 | * It turns out that the error enum slows things down a bit so for max speed you can `.map_err(|_| ())` if you don't care what type of parse error it is. 61 | 62 | Optimisations that didn't 63 | ========================= 64 | Things that didn't seem to have any effect: 65 | 66 | * Compiler breaks down *10 to *8 + *2 behind the scenes so we don't have to. 67 | (It seems to resort to imul for 100 so replacing shifts for that might make 68 | a slight gain) 69 | * casting len from usize to u8. 70 | 71 | FAQ 72 | === 73 | 74 | * Should I use this in production? As it's a new crate I would treat it with caution, 75 | there could still be a bug or two lurking despite tests and some light fuzzing. It's had one `reddit review` from which I've hopefully plugged a soundness hole around alignment. 76 | 77 | * Why not include this in the std library? The std library parsing code isn't radix specific. As the num parsing is in core, code size is important for embedded systems. This implementation is definitely more code than std. 78 | 79 | * If you want to run the tests under wasm you need to install: 80 | ``` 81 | cargo install wasm-bindgen-cli 82 | ``` 83 | 84 | TODO 85 | ==== 86 | 87 | - [x] make all the tests work in no_std mode. 88 | - [x] Compile for wasm 89 | - [ ] Bench on wasm? 90 | - [x] Run tests under wasm: `cargo test --features nightly --target=wasm32-unknown-unknown` 91 | - [x] have all wasm tests pass. 92 | - [ ] Wasm + Simd - can I use core simd and things just work with simulation for 256bits? 93 | - [ ] use no non-portable simd commands (so simd feature works on arm/neon). 94 | - [x] make work on big-endian 95 | 96 | ## Big-endien 97 | 98 | We can run the tests on big-endien via MIRI: 99 | 100 | ```sh 101 | rustup +nightly component add miri 102 | MIRIFLAGS="-Zmiri-symbolic-alignment-check" cargo miri test --target mips64-unknown-linux-gnuabi64 103 | ``` 104 | -------------------------------------------------------------------------------- /src/parse_u16.rs: -------------------------------------------------------------------------------- 1 | use super::{parse_2_chars, parse_4_chars, ParseIntError2, PLUS}; 2 | use core::num::IntErrorKind::*; 3 | 4 | type PIE = ParseIntError2; 5 | 6 | pub fn parse_u16(s: &[u8]) -> Result { 7 | let mut s = s; //.as_bytes(); 8 | let mut l: usize; 9 | let val = match s.get(0) { 10 | Some(&val) => { 11 | l = s.len(); 12 | if l == 1 { 13 | let val = val.wrapping_sub(b'0'); 14 | let v = val as u16; 15 | return if val <= 9 { 16 | Ok(v) 17 | } else { 18 | Err(PIE { kind: InvalidDigit }) 19 | }; 20 | } else if val != b'+' { 21 | val 22 | } else { 23 | s = &s[1..]; 24 | l -= 1; 25 | match s.get(0) { 26 | Some(&val) => { 27 | if l == 1 { 28 | let val = val.wrapping_sub(b'0'); 29 | return if val <= 9 { 30 | Ok(val as u16) 31 | } else { 32 | Err(PIE { kind: InvalidDigit }) 33 | }; 34 | } 35 | val 36 | } 37 | None => return Err(PIE { kind: InvalidDigit }), 38 | } 39 | } 40 | } 41 | None => return Err(PIE { kind: Empty }), 42 | }; 43 | 44 | match l { 45 | 2 => parse_2_chars(s), 46 | 3 => { 47 | let val = val.wrapping_sub(b'0'); 48 | let val1 = val as u16 * 100; 49 | if val <= 9 { 50 | return Ok(val1 + parse_2_chars(&s[1..])?); 51 | } else { 52 | return Err(PIE { kind: InvalidDigit }); 53 | }; 54 | } 55 | 4 => parse_4_chars(s), 56 | 5 => { 57 | let val = val.wrapping_sub(b'0'); 58 | let val1 = val as u16 * 10_000; 59 | let res = parse_4_chars(&s[1..])?; 60 | if val <= 6 { 61 | match val1.checked_add(res) { 62 | Some(val) => Ok(val), 63 | None => return Err(PIE { kind: PosOverflow }), 64 | } 65 | } else { 66 | Err(PIE { kind: PosOverflow }) 67 | } 68 | } 69 | _ => { 70 | let pos = s.iter().position(|byte| *byte != b'0'); 71 | if let Some(pos) = pos { 72 | if l - pos <= 5 { 73 | if s[pos] != b'+' { 74 | return parse_u16(&s[pos..]); 75 | } else { 76 | return Err(PIE { kind: InvalidDigit }); 77 | } 78 | } 79 | } else { 80 | return Ok(0); 81 | } 82 | return Err(PIE { kind: PosOverflow }); 83 | } 84 | } 85 | } 86 | 87 | pub fn parse_u16_challenger(s: &[u8]) -> Result { 88 | let mut s = s; //.as_bytes(); 89 | let (val, val2, val3) = match s.get(0) { 90 | Some(val) => { 91 | let mut val = val.wrapping_sub(b'0'); 92 | if val > 9 { 93 | if val == PLUS { 94 | s = &s[1..]; 95 | val = match s.get(0) { 96 | Some(val) => { 97 | let val = val.wrapping_sub(b'0'); 98 | if val > 9 { 99 | return Err(PIE { kind: InvalidDigit }); 100 | }; 101 | val 102 | } 103 | None => return Err(PIE { kind: InvalidDigit }), 104 | } 105 | } else { 106 | return Err(PIE { kind: InvalidDigit }); 107 | } 108 | } 109 | let val2 = match s.get(1) { 110 | None => { 111 | return Ok(val as u16); 112 | } 113 | Some(val2) => val2, 114 | }; 115 | let val3 = match s.get(2) { 116 | None => { 117 | let val2 = val2.wrapping_sub(b'0'); 118 | if val2 > 9 { 119 | return Err(PIE { kind: InvalidDigit }); 120 | } 121 | return Ok((val * 10 + val2) as u16); 122 | } 123 | Some(val3) => val3, 124 | }; 125 | (val, val2, val3) 126 | } 127 | None => return Err(PIE { kind: Empty }), 128 | }; 129 | let l = s.len(); 130 | // 111 131 | match l { 132 | 3 => { 133 | let val2 = val2.wrapping_sub(b'0'); 134 | let val3 = val3.wrapping_sub(b'0'); 135 | if (val2 > 9) | (val3 > 9) { 136 | return Err(PIE { kind: InvalidDigit }); 137 | } 138 | Ok(val as u16 * 100 + val2 as u16 * 10 + val3 as u16) 139 | } 140 | 4 => parse_4_chars(s), 141 | 5 => { 142 | if val > 6 { 143 | return Err(PIE { kind: PosOverflow }); 144 | } 145 | match (val as u16 * 10_000).checked_add(parse_4_chars(&s[1..])?) { 146 | Some(val) => Ok(val), 147 | None => return Err(PIE { kind: PosOverflow }), 148 | } 149 | } 150 | _ => { 151 | let pos = s.iter().position(|byte| *byte != b'0'); 152 | if let Some(pos) = pos { 153 | if l - pos <= 5 { 154 | if s[pos] != b'+' { 155 | return parse_u16(&s[pos..]); 156 | } else { 157 | return Err(PIE { kind: InvalidDigit }); 158 | } 159 | } 160 | } else { 161 | return Ok(0); 162 | } 163 | return Err(PIE { kind: PosOverflow }); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/trees.rs: -------------------------------------------------------------------------------- 1 | // Good sized christmas tree: 2 | pub(crate) const TENS_U8: &[u8] = &[1, 10, 100]; 3 | 4 | pub(crate) const TENS_U16: &[u16] = &[1, 10, 100, 1_000, 10_000]; 5 | 6 | pub(crate) const TENS_U32: &[u32] = &[ 7 | 1, 8 | 10, 9 | 100, 10 | 1_000, 11 | 10_000, 12 | 100_000, 13 | 1_000_000, 14 | 10_000_000, 15 | 100_000_000, 16 | 1_000_000_000, 17 | ]; 18 | 19 | // Good sized christmas tree: 20 | pub(crate) const TENS_U64: &[u64] = &[ 21 | 1, 22 | 10, 23 | 100, 24 | 1_000, 25 | 10_000, 26 | 100_000, 27 | 1_000_000, 28 | 10_000_000, 29 | 100_000_000, 30 | 1_000_000_000, 31 | 10_000_000_000, 32 | 100_000_000_000, 33 | 1_000_000_000_000, 34 | 10_000_000_000_000, 35 | 100_000_000_000_000, 36 | 1_000_000_000_000_000, 37 | 10_000_000_000_000_000, 38 | 100_000_000_000_000_000, 39 | 1_000_000_000_000_000_000, 40 | 10_000_000_000_000_000_000, 41 | ]; 42 | 43 | #[cfg(target_pointer_width = "16")] 44 | pub(crate) const TENS_USIZE: &[usize] = &[1, 10, 100, 1_000, 10_000]; 45 | 46 | #[cfg(target_pointer_width = "32")] 47 | pub(crate) const TENS_USIZE: &[usize] = &[ 48 | 1, 49 | 10, 50 | 100, 51 | 1_000, 52 | 10_000, 53 | 100_000, 54 | 1_000_000, 55 | 10_000_000, 56 | 100_000_000, 57 | 1_000_000_000, 58 | ]; 59 | 60 | #[cfg(target_pointer_width = "64")] 61 | pub(crate) const TENS_USIZE: &[usize] = &[ 62 | 1, 63 | 10, 64 | 100, 65 | 1_000, 66 | 10_000, 67 | 100_000, 68 | 1_000_000, 69 | 10_000_000, 70 | 100_000_000, 71 | 1_000_000_000, 72 | 10_000_000_000, 73 | 100_000_000_000, 74 | 1_000_000_000_000, 75 | 10_000_000_000_000, 76 | 100_000_000_000_000, 77 | 1_000_000_000_000_000, 78 | 10_000_000_000_000_000, 79 | 100_000_000_000_000_000, 80 | 1_000_000_000_000_000_000, 81 | 10_000_000_000_000_000_000, 82 | ]; 83 | 84 | // Biggest christmas tree: 85 | pub(crate) const TENS_U128: &[u128] = &[ 86 | 1, 87 | 10, 88 | 100, 89 | 1_000, 90 | 10_000, 91 | 100_000, 92 | 1_000_000, 93 | 10_000_000, 94 | 100_000_000, 95 | 1_000_000_000, 96 | 10_000_000_000, 97 | 100_000_000_000, 98 | 1_000_000_000_000, 99 | 10_000_000_000_000, 100 | 100_000_000_000_000, 101 | 1_000_000_000_000_000, 102 | 10_000_000_000_000_000, 103 | 100_000_000_000_000_000, 104 | 1_000_000_000_000_000_000, 105 | 10_000_000_000_000_000_000, 106 | 100_000_000_000_000_000_000, 107 | 1_000_000_000_000_000_000_000, 108 | 10_000_000_000_000_000_000_000, 109 | 100_000_000_000_000_000_000_000, 110 | 1_000_000_000_000_000_000_000_000, 111 | 10_000_000_000_000_000_000_000_000, 112 | 100_000_000_000_000_000_000_000_000, 113 | 1_000_000_000_000_000_000_000_000_000, 114 | 10_000_000_000_000_000_000_000_000_000, 115 | 100_000_000_000_000_000_000_000_000_000, 116 | 1_000_000_000_000_000_000_000_000_000_000, 117 | 10_000_000_000_000_000_000_000_000_000_000, 118 | 100_000_000_000_000_000_000_000_000_000_000, 119 | 1_000_000_000_000_000_000_000_000_000_000_000, 120 | 10_000_000_000_000_000_000_000_000_000_000_000, 121 | 100_000_000_000_000_000_000_000_000_000_000_000, 122 | 1_000_000_000_000_000_000_000_000_000_000_000_000, 123 | 10_000_000_000_000_000_000_000_000_000_000_000_000, 124 | 100_000_000_000_000_000_000_000_000_000_000_000_000, 125 | ]; 126 | 127 | pub(crate) const TENS_I8: &[i8] = &[1, 10, 100]; 128 | 129 | pub(crate) const TENS_I16: &[i16] = &[1, 10, 100, 1_000, 10_000]; 130 | 131 | pub(crate) const TENS_I32: &[i32] = &[ 132 | 1, 133 | 10, 134 | 100, 135 | 1_000, 136 | 10_000, 137 | 100_000, 138 | 1_000_000, 139 | 10_000_000, 140 | 100_000_000, 141 | 1_000_000_000, 142 | ]; 143 | 144 | // Good sized christmas tree: 145 | pub(crate) const TENS_I64: &[i64] = &[ 146 | 1, 147 | 10, 148 | 100, 149 | 1_000, 150 | 10_000, 151 | 100_000, 152 | 1_000_000, 153 | 10_000_000, 154 | 100_000_000, 155 | 1_000_000_000, 156 | 10_000_000_000, 157 | 100_000_000_000, 158 | 1_000_000_000_000, 159 | 10_000_000_000_000, 160 | 100_000_000_000_000, 161 | 1_000_000_000_000_000, 162 | 10_000_000_000_000_000, 163 | 100_000_000_000_000_000, 164 | 1_000_000_000_000_000_000, 165 | ]; 166 | 167 | #[cfg(target_pointer_width = "16")] 168 | pub(crate) const TENS_ISIZE: &[isize] = &[1, 10, 100, 1_000, 10_000]; 169 | 170 | #[cfg(target_pointer_width = "32")] 171 | pub(crate) const TENS_ISIZE: &[isize] = &[ 172 | 1, 173 | 10, 174 | 100, 175 | 1_000, 176 | 10_000, 177 | 100_000, 178 | 1_000_000, 179 | 10_000_000, 180 | 100_000_000, 181 | 1_000_000_000, 182 | ]; 183 | 184 | #[cfg(target_pointer_width = "64")] 185 | pub(crate) const TENS_ISIZE: &[isize] = &[ 186 | 1, 187 | 10, 188 | 100, 189 | 1_000, 190 | 10_000, 191 | 100_000, 192 | 1_000_000, 193 | 10_000_000, 194 | 100_000_000, 195 | 1_000_000_000, 196 | 10_000_000_000, 197 | 100_000_000_000, 198 | 1_000_000_000_000, 199 | 10_000_000_000_000, 200 | 100_000_000_000_000, 201 | 1_000_000_000_000_000, 202 | 10_000_000_000_000_000, 203 | 100_000_000_000_000_000, 204 | 1_000_000_000_000_000_000, 205 | ]; 206 | 207 | // Biggest christmas tree: 208 | pub(crate) const TENS_I128: &[i128] = &[ 209 | 1, 210 | 10, 211 | 100, 212 | 1_000, 213 | 10_000, 214 | 100_000, 215 | 1_000_000, 216 | 10_000_000, 217 | 100_000_000, 218 | 1_000_000_000, 219 | 10_000_000_000, 220 | 100_000_000_000, 221 | 1_000_000_000_000, 222 | 10_000_000_000_000, 223 | 100_000_000_000_000, 224 | 1_000_000_000_000_000, 225 | 10_000_000_000_000_000, 226 | 100_000_000_000_000_000, 227 | 1_000_000_000_000_000_000, 228 | 10_000_000_000_000_000_000, 229 | 100_000_000_000_000_000_000, 230 | 1_000_000_000_000_000_000_000, 231 | 10_000_000_000_000_000_000_000, 232 | 100_000_000_000_000_000_000_000, 233 | 1_000_000_000_000_000_000_000_000, 234 | 10_000_000_000_000_000_000_000_000, 235 | 100_000_000_000_000_000_000_000_000, 236 | 1_000_000_000_000_000_000_000_000_000, 237 | 10_000_000_000_000_000_000_000_000_000, 238 | 100_000_000_000_000_000_000_000_000_000, 239 | 1_000_000_000_000_000_000_000_000_000_000, 240 | 10_000_000_000_000_000_000_000_000_000_000, 241 | 100_000_000_000_000_000_000_000_000_000_000, 242 | 1_000_000_000_000_000_000_000_000_000_000_000, 243 | 10_000_000_000_000_000_000_000_000_000_000_000, 244 | 100_000_000_000_000_000_000_000_000_000_000_000, 245 | 1_000_000_000_000_000_000_000_000_000_000_000_000, 246 | 10_000_000_000_000_000_000_000_000_000_000_000_000, 247 | 100_000_000_000_000_000_000_000_000_000_000_000_000, 248 | ]; 249 | -------------------------------------------------------------------------------- /src/parse_i32.rs: -------------------------------------------------------------------------------- 1 | use super::{parse_2_chars, parse_4_chars, parse_8_chars, ParseIntError2, MINUS, PLUS}; 2 | use core::num::IntErrorKind::*; 3 | 4 | type PIE = ParseIntError2; 5 | 6 | /// Parses from -2_147_483_648 to 2_147_483_647 (10 digits and optionally +/-) 7 | pub fn parse_i32_fails(s: &[u8]) -> Result { 8 | let mut s = s; //.as_bytes(); 9 | let val = match s.get(0) { 10 | Some(val) => { 11 | let val = val.wrapping_sub(b'0'); 12 | if val <= 9 { 13 | val 14 | } else { 15 | if val == MINUS { 16 | s = &s[1..]; 17 | let val = match s.get(0) { 18 | Some(val) => { 19 | let val = val.wrapping_sub(b'0'); 20 | if val <= 9 { 21 | val 22 | } else { 23 | return Err(PIE { kind: InvalidDigit }); 24 | } 25 | } 26 | None => return Err(PIE { kind: Empty }), 27 | }; 28 | let l = s.len(); 29 | unsafe { 30 | return match l { 31 | 1 => Ok(-(val as i32)), 32 | 2 => { 33 | let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 34 | if val2 <= 9 { 35 | Ok(-((val * 10 + val2) as i32)) 36 | } else { 37 | Err(PIE { kind: InvalidDigit }) 38 | } 39 | } 40 | 3 => { 41 | let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 42 | let val3 = s.get_unchecked(2).wrapping_sub(b'0'); 43 | if (val2 <= 9) & (val3 <= 9) { 44 | Ok(-(val as i32 * 100 + (val2 * 10 + val3) as i32)) 45 | } else { 46 | Err(PIE { kind: InvalidDigit }) 47 | } 48 | } 49 | 4 => Ok(-(parse_4_chars(s)? as i32)), 50 | 5 => Ok(-(val as i32 * 1_0000 + parse_4_chars(&s[1..])? as i32)), 51 | 6 => { 52 | let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 53 | if val2 <= 9 { 54 | let result = parse_4_chars(&s[2..])? as i32; 55 | Ok(-(val as i32 * 10_0000 + val2 as i32 * 1_0000 + result)) 56 | } else { 57 | Err(PIE { kind: InvalidDigit }) 58 | } 59 | } 60 | 7 => { 61 | let val2 = parse_4_chars(&s[1..])? as i32 * 100; 62 | let val3 = parse_2_chars(&s[5..])? as i32; 63 | Ok(-(val as i32 * 1_000_000 + val2 + val3)) 64 | } 65 | 8 => parse_8_chars(&s).map(|val| -(val as i32)), 66 | 9 => { 67 | let result = parse_8_chars(&s[1..])? as i32; 68 | Ok(-(result + (val as i32 * 100_000_000))) 69 | } 70 | 10 => { 71 | let mut val2 = s.get_unchecked(1).wrapping_sub(b'0') as i32; 72 | if (val <= 2) & (val2 <= 9) { 73 | let mut result = parse_8_chars(&s[2..])? as i32; 74 | let val = val as i32 * 1_000_000_000; 75 | val2 *= 100_000_000; 76 | result += val2; 77 | if result != 147483648 { 78 | match result.checked_add(val) { 79 | Some(val) => Ok(-(val)), 80 | None => Err(PIE { kind: NegOverflow }), 81 | } 82 | } else { 83 | return Ok(i32::MIN); 84 | } 85 | } else { 86 | return Err(PIE { kind: NegOverflow }); 87 | } 88 | } 89 | _ => Err(PIE { kind: InvalidDigit }), 90 | }; 91 | } 92 | } 93 | if val == PLUS { 94 | s = &s[1..]; 95 | match s.get(0) { 96 | Some(val2) => { 97 | let val2 = (*val2).wrapping_sub(b'0'); 98 | if val2 <= 9 { 99 | val2 100 | } else { 101 | return Err(PIE { kind: InvalidDigit }); 102 | } 103 | } 104 | None => return Err(PIE { kind: InvalidDigit }), 105 | } 106 | } else { 107 | return Err(PIE { kind: InvalidDigit }); 108 | } 109 | } 110 | } 111 | None => return Err(PIE { kind: Empty }), 112 | }; 113 | let l = s.len(); 114 | unsafe { 115 | match l { 116 | 1 => Ok(val as i32), 117 | 2 => { 118 | let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 119 | if val2 <= 9 { 120 | Ok((val * 10 + val2) as i32) 121 | } else { 122 | Err(PIE { kind: InvalidDigit }) 123 | } 124 | } 125 | 3 => { 126 | let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 127 | let val3 = s.get_unchecked(2).wrapping_sub(b'0'); 128 | if (val2 <= 9) & (val3 <= 9) { 129 | Ok(val as i32 * 100 + (val2 * 10 + val3) as i32) 130 | } else { 131 | Err(PIE { kind: InvalidDigit }) 132 | } 133 | } 134 | 4 => Ok(parse_4_chars(s)? as i32), 135 | 5 => Ok(val as i32 * 1_0000 + parse_4_chars(&s[1..])? as i32), 136 | 6 => { 137 | let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 138 | if val2 <= 9 { 139 | let result = parse_4_chars(&s[2..])? as i32; 140 | Ok(val as i32 * 10_0000 + val2 as i32 * 1_0000 + result) 141 | } else { 142 | Err(PIE { kind: InvalidDigit }) 143 | } 144 | } 145 | 7 => { 146 | let val2 = parse_4_chars(&s[1..])? as i32 * 100; 147 | let val3 = parse_2_chars(&s[5..])? as i32; 148 | Ok(val as i32 * 1_000_000 + val2 + val3) 149 | } 150 | 8 => parse_8_chars(&s).map(|val| val as i32), 151 | 9 => { 152 | let result = parse_8_chars(&s[1..])? as i32; 153 | Ok(result + (val as i32 * 100_000_000)) 154 | } 155 | 10 => { 156 | let mut val2 = s.get_unchecked(1).wrapping_sub(b'0') as i32; 157 | if (val <= 2) & (val2 <= 9) { 158 | let mut result = parse_8_chars(&s[2..])? as i32; 159 | let val = val as i32 * 1_000_000_000; 160 | val2 *= 100_000_000; 161 | result += val; 162 | match result.checked_add(val2) { 163 | Some(val) => Ok(val), 164 | None => Err(PIE { kind: PosOverflow }), 165 | } 166 | } else { 167 | return Err(PIE { kind: PosOverflow }); 168 | } 169 | } 170 | _ => { 171 | let pos = s.iter().position(|byte| *byte != b'0'); 172 | if let Some(pos) = pos { 173 | if l - pos <= 11 { 174 | if s[pos] != b'+' { 175 | return parse_i32(&s[pos..]); 176 | } else { 177 | return Err(PIE { kind: InvalidDigit }); 178 | } 179 | } 180 | } else { 181 | return Ok(0); 182 | } 183 | return Err(PIE { kind: PosOverflow }); 184 | } 185 | } 186 | } 187 | } 188 | 189 | pub fn parse_i32(s: &[u8]) -> Result { 190 | super::parse::(s).map_err(|_| PIE { kind: InvalidDigit }) 191 | } 192 | 193 | pub fn parse_i32_challenger(s: &[u8]) -> Result { 194 | super::parse::(s).map_err(|_| PIE { kind: InvalidDigit }) 195 | } 196 | -------------------------------------------------------------------------------- /src/parse_u8.rs: -------------------------------------------------------------------------------- 1 | use crate::parse_2_chars; 2 | 3 | use super::ParseIntError2; 4 | use core::num::IntErrorKind::*; 5 | 6 | type PIE = ParseIntError2; 7 | 8 | /// Parse from "0" to "+255" and "+00000000255" 9 | pub fn parse_u8(mut s: &[u8]) -> Result { 10 | loop { 11 | match s.len() { 12 | 1 => { 13 | // 0 14 | let val = s[0].wrapping_sub(b'0'); 15 | if val <= 9 { 16 | return Ok(val); 17 | } else { 18 | return Err(PIE { kind: InvalidDigit }); 19 | } 20 | } 21 | 3 => { 22 | // +00, 100 23 | let val23 = parse_2_chars(unsafe { &s.get_unchecked(1..) })? as u8; 24 | let a = 100 + val23; 25 | let val = unsafe { *s.get_unchecked(0) }; 26 | if val == b'1' { 27 | return Ok(a); 28 | } else if val == b'2' { 29 | if val23 <= 55 { 30 | return Ok(200 + val23); 31 | } else { 32 | return Err(PIE { kind: PosOverflow }); 33 | } 34 | } else if val == b'+' || val == b'0' { 35 | return Ok(val23); 36 | } else { 37 | return Err(PIE { kind: PosOverflow }); 38 | } 39 | } 40 | 2 => { 41 | // +0, 10 42 | match parse_2_chars(s) { 43 | Ok(val) => return Ok(val as u8), 44 | Err(_) => { 45 | if s[0] == b'+' { 46 | let val = s[1].wrapping_sub(b'0'); 47 | if val <= 9 { 48 | return Ok(val); 49 | } 50 | } 51 | return Err(PIE { kind: InvalidDigit }); 52 | } 53 | } 54 | } 55 | _ => match s.get(0) { 56 | Some(val) => { 57 | if *val == b'+' { 58 | s = &s[1..]; 59 | } 60 | match s.iter().position(|ch| *ch != b'0') { 61 | Some(pos) => { 62 | s = &s[pos..]; 63 | if s[0] == b'+' || s.len() > 3 { 64 | return Err(PIE { kind: PosOverflow }); 65 | } 66 | } 67 | None => { 68 | return Ok(0); 69 | } 70 | } 71 | } 72 | None => { 73 | return Err(PIE { kind: Empty }); 74 | } 75 | }, 76 | } 77 | } 78 | } 79 | 80 | /// Parse from "0" to "+255" and "+00000000255" 81 | // pub fn parse_u8_challenger(s: &[u8]) -> Result { 82 | // let mut iter = s.iter(); 83 | // match iter.next() { 84 | // Some(val) => { 85 | // let mut val = val.wrapping_sub(b'0'); 86 | // if val > 9 { 87 | // if val == PLUS { 88 | // // '+' - '0' = 251 89 | // match iter.next() { 90 | // Some(alt_val) => { 91 | // val = alt_val.wrapping_sub(b'0'); 92 | // if val > 9 { 93 | // return Err(PIE { kind: InvalidDigit }); 94 | // } 95 | // } 96 | // None => return Err(PIE { kind: InvalidDigit }), 97 | // } 98 | // } else { 99 | // return Err(PIE { kind: InvalidDigit }); 100 | // } 101 | // } 102 | // while val == 0 { 103 | // val = match iter.next() { 104 | // Some(val2) => { 105 | // val2.wrapping_sub(b'0') 106 | // } 107 | // None => { return Ok(0); } 108 | // } 109 | // } 110 | // match iter.next() { 111 | // Some(val2) => { 112 | // let val2 = val2.wrapping_sub(b'0'); 113 | // if val2 <= 9 { 114 | // match iter.next() { 115 | // Some(val3) => match iter.next() { 116 | // None => { 117 | // let val3 = val3.wrapping_sub(b'0'); 118 | // let val2 = val2 * 10; 119 | // if val3 <= 9 { 120 | // match val { 121 | // 1 => Ok(100 + val2 + val3), 122 | // 2 => { 123 | // let two = val2 + val3; 124 | // if two <= 55 { 125 | // Ok(200 + two) 126 | // } else { 127 | // Err(PIE { kind: PosOverflow }) 128 | // } 129 | // } 130 | // 0 => Ok(val2 + val3), 131 | // _ => Err(PIE { kind: PosOverflow }), 132 | // } 133 | // } else { 134 | // return Err(PIE { kind: InvalidDigit }); 135 | // } 136 | // } 137 | // Some(_) => return Err(PIE { kind: PosOverflow }), 138 | // }, 139 | // None => Ok(val * 10 + val2), 140 | // } 141 | // } else { 142 | // return Err(PIE { kind: InvalidDigit }); 143 | // } 144 | // } 145 | // None => Ok(val), 146 | // } 147 | // } 148 | // _ => Err(PIE { kind: Empty }), 149 | // } 150 | // } 151 | 152 | /// Parse from "0" to "+255" 153 | pub fn parse_u8_challenger(s: &[u8]) -> Result { 154 | let mut iter = s.iter(); 155 | match iter.next() { 156 | Some(mut val) => { 157 | if *val == b'+' { 158 | match iter.next() { 159 | Some(alt_val) => { 160 | val = alt_val; 161 | } 162 | None => return Err(PIE { kind: InvalidDigit }), 163 | } 164 | } 165 | let mut val = val.wrapping_sub(b'0'); 166 | while val == 0 { 167 | val = match iter.next() { 168 | Some(val2) => { 169 | let val2 = val2.wrapping_sub(b'0'); 170 | if val2 > 9 { 171 | return Err(PIE { kind: InvalidDigit }); 172 | } 173 | val2 174 | } 175 | None => { 176 | return Ok(0); 177 | } 178 | }; 179 | } 180 | match iter.next() { 181 | Some(val2) => { 182 | let val2 = val2.wrapping_sub(b'0'); 183 | if val2 > 9 { 184 | return Err(PIE { kind: InvalidDigit }); 185 | } 186 | match iter.next() { 187 | Some(val3) => match iter.next() { 188 | None => { 189 | let val3 = val3.wrapping_sub(b'0'); 190 | let val2 = val2 * 10; 191 | if val3 <= 9 { 192 | match val { 193 | 1 => Ok(100 + val2 + val3), 194 | 2 => { 195 | let two = val2 + val3; 196 | if two <= 55 { 197 | Ok(200 + two) 198 | } else { 199 | Err(PIE { kind: PosOverflow }) 200 | } 201 | } 202 | 0 => Ok(val2 + val3), 203 | _ => Err(PIE { kind: PosOverflow }), 204 | } 205 | } else { 206 | return Err(PIE { kind: InvalidDigit }); 207 | } 208 | } 209 | Some(_) => return Err(PIE { kind: PosOverflow }), 210 | }, 211 | None => { 212 | if val <= 9 { 213 | Ok(val * 10 + val2) 214 | } else { 215 | Err(PIE { kind: InvalidDigit }) 216 | } 217 | } 218 | } 219 | } 220 | None => { 221 | if val <= 9 { 222 | Ok(val) 223 | } else { 224 | Err(PIE { kind: InvalidDigit }) 225 | } 226 | } 227 | } 228 | } 229 | _ => Err(PIE { kind: Empty }), 230 | } 231 | } 232 | 233 | // /// Parse from "0" to "+255" 234 | // pub fn parse_u8_old_best(s: &str) -> Result { 235 | // let mut s = s.as_bytes(); 236 | // let first = s.get(0); 237 | // match first { 238 | // Some(val) if *val == b'+' => s = &s[1..], 239 | // Some(val) => { 240 | // if s.len() == 1 { 241 | // let result = val.wrapping_sub(b'0'); 242 | // return if result <= 9 { Ok(result) } else { Err(()) }; 243 | // } 244 | // } 245 | // _ => {} 246 | // }; 247 | // let l = s.len(); 248 | // return match l { 249 | // 2 => { 250 | // let result = parse_2_chars(s)?; 251 | // Ok(result as u8) 252 | // } 253 | // 3 => { 254 | // let val = s[0].wrapping_sub(b'0'); 255 | // if val <= 2 { 256 | // match (val * 100).checked_add(parse_2_chars(&s[1..])? as u8) { 257 | // Some(val) => Ok(val), 258 | // None => Err(()), 259 | // } 260 | // } else { 261 | // Err(()) 262 | // } 263 | // } 264 | // _ => Err(()), 265 | // }; 266 | // } 267 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | use criterion::BenchmarkId; 2 | use criterion::Throughput; 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | use std::hint::black_box; 5 | //use criterion_cycles_per_byte::CyclesPerByte; 6 | use paste::paste; 7 | 8 | use atoi_radix10::*; 9 | 10 | pub fn std_parse(s: &str) -> Result 11 | where 12 | T: core::str::FromStr, 13 | { 14 | s.parse().map_err(|_| ()) 15 | } 16 | 17 | macro_rules! ok_bench { 18 | ($target_type:ty, $prefix:literal, $values:expr) => { 19 | paste! { 20 | fn [](c: &mut Criterion// 21 | ) { 22 | let mut group = c.benchmark_group(stringify!([<$prefix $target_type>])); 23 | 24 | group //.sample_size(30) 25 | .warm_up_time(core::time::Duration::from_millis(500)); 26 | // .measurement_time(core::time::Duration::from_millis(2000)); 27 | for num_str in $values.iter() { 28 | let num : $target_type = num_str.parse().unwrap(); 29 | group.throughput(Throughput::Bytes(num_str.len() as u64)); 30 | group.bench_with_input(BenchmarkId::new(format!("{}std",$prefix), num), &num_str, |b, &val| { 31 | b.iter(|| std_parse::<$target_type>(&val).map_err(|_| ())); 32 | }); 33 | // group.bench_with_input(BenchmarkId::new(format!("{}challenger",$prefix), num), &num_str, |b, &val| { 34 | // b.iter(|| $challenger_meth(val.as_bytes())); 35 | // }); 36 | group.bench_with_input(BenchmarkId::new(format!("{}generic",$prefix), num), &num_str, |b, &val| { 37 | b.iter(|| atoi_radix10::parse::<$target_type>(val.as_bytes()).map_err(|_| ())); 38 | }); 39 | // group.bench_with_input(BenchmarkId::new(format!("{}challenger",$prefix), num), &num_str, |b, &val| { 40 | // b.iter(|| atoi_radix10::parse_challenger::<$target_type>(val.as_bytes())); 41 | // }); 42 | //super::parse::(s).map_err(|_| PIE { kind: InvalidDigit }) 43 | // group.bench_with_input(BenchmarkId::new(format!("{}atoi_radix10",$prefix), num), &num_str, |b, &val| { 44 | // b.iter(|| $meth(val.as_bytes())); 45 | // }); 46 | assert_eq!(atoi_radix10::parse::<$target_type>(num_str.as_bytes()), Ok(num), " when atoi_radix10 parsing {}", num_str); 47 | assert_eq!(atoi_radix10::parse_challenger::<$target_type>(num_str.as_bytes()), Ok(num), " when challenger parsing {}", num_str); 48 | } 49 | group.finish(); 50 | } 51 | } 52 | }; 53 | } 54 | 55 | fn parse_chars_bench(c: &mut Criterion) { 56 | let mut group = c.benchmark_group("parse_chars_bench_group"); 57 | //group //.sample_size(30) 58 | // .warm_up_time(core::time::Duration::from_millis(1000)) 59 | // .measurement_time(core::time::Duration::from_millis(2000)); 60 | let num_str = "12345678123456781234567812345678"; 61 | assert_eq!( 62 | unsafe { parse_32_chars(num_str.as_bytes()) }, 63 | Ok(12345678123456781234567812345678) 64 | ); 65 | group.throughput(Throughput::Bytes(num_str.len() as u64)); 66 | group.bench_with_input(BenchmarkId::new("32", num_str), &num_str, |b, &_val| { 67 | b.iter(|| unsafe { parse_32_chars(black_box(num_str.as_bytes())) }); 68 | }); 69 | let num_str = "1234567812345678"; 70 | assert_eq!( 71 | unsafe { parse_16_chars(num_str.as_bytes()) }, 72 | Ok(1234567812345678) 73 | ); 74 | group.throughput(Throughput::Bytes(num_str.len() as u64)); 75 | group.bench_with_input(BenchmarkId::new("16", num_str), &num_str, |b, &_val| { 76 | b.iter(|| unsafe { parse_16_chars(black_box(num_str.as_bytes())) }); 77 | }); 78 | let num_str = "12345678"; 79 | assert_eq!(unsafe { parse_8_chars(num_str.as_bytes()) }, Ok(12345678)); 80 | group.throughput(Throughput::Bytes(num_str.len() as u64)); 81 | group.bench_with_input(BenchmarkId::new("8", num_str), &num_str, |b, &_val| { 82 | b.iter(|| unsafe { parse_8_chars(black_box(num_str.as_bytes())) }); 83 | }); 84 | // let num_str = "123456"; 85 | // assert_eq!(parse_6_chars(num_str.as_bytes()), Ok(123456)); 86 | // group.throughput(Throughput::Bytes(num_str.len() as u64)); 87 | // group.bench_with_input(BenchmarkId::new("6", num_str), &num_str, |b, &_val| { 88 | // b.iter(|| parse_6_chars(black_box(num_str.as_bytes()))); 89 | // }); 90 | let num_str = "1234"; 91 | assert_eq!(unsafe { parse_4_chars(num_str.as_bytes()) }, Ok(1234)); 92 | group.throughput(Throughput::Bytes(num_str.len() as u64)); 93 | group.bench_with_input(BenchmarkId::new("4", num_str), &num_str, |b, &_val| { 94 | b.iter(|| unsafe { parse_4_chars(black_box(num_str.as_bytes())) }); 95 | }); 96 | let num_str = "12"; 97 | assert_eq!(unsafe { parse_2_chars(num_str.as_bytes()) }, Ok(12)); 98 | group.throughput(Throughput::Bytes(num_str.len() as u64)); 99 | group.bench_with_input(BenchmarkId::new("2", num_str), &num_str, |b, &_val| { 100 | b.iter(|| unsafe { parse_2_chars(black_box(num_str.as_bytes())) }); 101 | }); 102 | 103 | group.finish(); 104 | } 105 | 106 | ok_bench!(u8, "", ["1", "12", "123", "+200", &u8::MAX.to_string()]); 107 | 108 | ok_bench!(i8, "pos_", ["1", "12", "123", "+100", &i8::MAX.to_string()]); 109 | 110 | ok_bench!(i8, "neg_", [&i8::MIN.to_string(), "-12", "-1"]); 111 | 112 | ok_bench!(u16, "", ["1", "12", "123", "1234", "12345",]); 113 | 114 | ok_bench!( 115 | i16, 116 | "pos_", 117 | //[&i16::MIN.to_string(), "-1234", "-123", "-12","-1", "1", "12", "123", "1234", &i16::MAX.to_string(),"+12345"] 118 | //[&i16::MIN.to_string(), "-1234", "-123", "-12","-1"] 119 | ["1", "12", "123", "1234", "12345", "+12345"] 120 | ); 121 | 122 | ok_bench!( 123 | i16, 124 | "neg_", 125 | //[&i16::MIN.to_string(), "-1234", "-123", "-12","-1", "1", "12", "123", "1234", &i16::MAX.to_string(),"+12345"] 126 | [&i16::MIN.to_string(), "-1234", "-123", "-12", "-1"] //["1", "12", "123", "1234", &i16::MAX.to_string(),"+12345"] 127 | ); 128 | 129 | ok_bench!( 130 | u32, 131 | "", 132 | [ 133 | "1", 134 | "12", 135 | "123", 136 | "1234", 137 | "12345", 138 | "123456", 139 | "1234567", 140 | "12345678", 141 | "123456789", 142 | &u32::MAX.to_string() 143 | ] 144 | ); 145 | 146 | ok_bench!( 147 | i32, 148 | "pos_", 149 | [ 150 | "1", 151 | "12", 152 | "123", 153 | "1234", 154 | "12345", 155 | "123456", 156 | "1234567", 157 | "12345678", 158 | "123456789", 159 | &i32::MAX.to_string() 160 | ] 161 | ); 162 | 163 | ok_bench!( 164 | i32, 165 | "neg_", 166 | [ 167 | "-1", 168 | "-12", 169 | "-123", 170 | "-1234", 171 | "-12345", 172 | "-123456", 173 | "-1234567", 174 | "-12345678", 175 | "-123456789", 176 | &i32::MIN.to_string() 177 | ] 178 | ); 179 | 180 | ok_bench!( 181 | u64, 182 | "", 183 | [ 184 | "1", 185 | "12", 186 | "123", 187 | "1234", 188 | "12345", 189 | "123456", 190 | "1234567", 191 | "12345678", 192 | "123456789", 193 | "1234567890", 194 | "12345678901", 195 | "123456789012", 196 | "1234567890123", 197 | "12345678901234", 198 | "123456789012345", 199 | "1234567890123456", 200 | "12345678901234567", 201 | "123456789012345678", 202 | "1234567890123456789", 203 | &u64::MAX.to_string() 204 | ] 205 | ); 206 | 207 | ok_bench!( 208 | i64, 209 | "pos_", 210 | [ 211 | "1", 212 | "12", 213 | "123", 214 | "1234", 215 | "12345", 216 | "123456", 217 | "1234567", 218 | "12345678", 219 | "123456789", 220 | "1234567890", 221 | "12345678901", 222 | "123456789012", 223 | "1234567890123", 224 | "12345678901234", 225 | "123456789012345", 226 | "1234567890123456", 227 | "12345678901234567", 228 | "123456789012345678", 229 | "1234567890123456789", 230 | &i64::MAX.to_string() 231 | ] 232 | ); 233 | 234 | ok_bench!( 235 | i64, 236 | "neg_", 237 | [ 238 | "-1", 239 | "-12", 240 | "-123", 241 | "-1234", 242 | "-12345", 243 | "-123456", 244 | "-1234567", 245 | "-12345678", 246 | "-123456789", 247 | "-1234567890", 248 | "-12345678901", 249 | "-123456789012", 250 | "-1234567890123", 251 | "-12345678901234", 252 | "-123456789012345", 253 | "-1234567890123456", 254 | "-12345678901234567", 255 | "-123456789012345678", 256 | "-1234567890123456789", 257 | &i64::MIN.to_string() 258 | ] 259 | ); 260 | 261 | ok_bench!( 262 | u128, 263 | "", 264 | [ 265 | "1", 266 | "12", 267 | "123", 268 | "12345", 269 | "1234567", 270 | "123456789", 271 | "12345678901", 272 | "1234567890123", 273 | "123456789012345", 274 | "12345678901234567", 275 | "1234567890123456789", 276 | "123456789012345678901", 277 | "12345678901234567890123", 278 | "1234567890123456789012345", 279 | "123456789012345678901234567", 280 | "12345678901234567890123456789", 281 | "1234567890123456789012345678901", 282 | "123456789012345678901234567890123", 283 | "12345678901234567890123456789012345", 284 | "1234567890123456789012345678901234567", 285 | "123456789012345678901234567890123456789", 286 | &u128::MAX.to_string() 287 | ] 288 | ); 289 | 290 | ok_bench!( 291 | i128, 292 | "pos_", 293 | [ 294 | "1", 295 | "12", 296 | "123", 297 | "12345", 298 | "1234567", 299 | "123456789", 300 | "12345678901", 301 | "1234567890123", 302 | "123456789012345", 303 | "12345678901234567", 304 | "1234567890123456789", 305 | "123456789012345678901", 306 | "12345678901234567890123", 307 | "1234567890123456789012345", 308 | "123456789012345678901234567", 309 | "12345678901234567890123456789", 310 | "1234567890123456789012345678901", 311 | "123456789012345678901234567890123", 312 | "12345678901234567890123456789012345", 313 | "1234567890123456789012345678901234567", 314 | "123456789012345678901234567890123456789", 315 | &i128::MAX.to_string() 316 | ] 317 | ); 318 | 319 | ok_bench!( 320 | i128, 321 | "neg_", 322 | [ 323 | "-1", 324 | "-12", 325 | "-123", 326 | "-12345", 327 | "-1234567", 328 | "-123456789", 329 | "-12345678901", 330 | "-1234567890123", 331 | "-123456789012345", 332 | "-12345678901234567", 333 | "-1234567890123456789", 334 | "-123456789012345678901", 335 | "-12345678901234567890123", 336 | "-1234567890123456789012345", 337 | "-123456789012345678901234567", 338 | "-12345678901234567890123456789", 339 | "-1234567890123456789012345678901", 340 | "-123456789012345678901234567890123", 341 | "-12345678901234567890123456789012345", 342 | "-1234567890123456789012345678901234567", 343 | "-123456789012345678901234567890123456789", 344 | &i128::MIN.to_string() 345 | ] 346 | ); 347 | 348 | criterion_group!( 349 | name = benches; 350 | config = Criterion::default();//.with_measurement(CyclesPerByte); 351 | targets = 352 | bench_parse_u8, 353 | bench_parse_u16, 354 | bench_parse_u32, 355 | bench_parse_u64, 356 | bench_parse_u128, 357 | bench_parse_pos_i8, 358 | bench_parse_neg_i8, 359 | bench_parse_pos_i16, 360 | bench_parse_neg_i16, 361 | bench_parse_pos_i32, 362 | bench_parse_neg_i32, 363 | bench_parse_pos_i64, 364 | bench_parse_neg_i64, 365 | bench_parse_pos_i128, 366 | bench_parse_neg_i128, 367 | parse_chars_bench, 368 | ); 369 | 370 | criterion_main!(benches); 371 | -------------------------------------------------------------------------------- /src/parse_i16.rs: -------------------------------------------------------------------------------- 1 | use super::{parse_4_chars, ParseIntError2, MINUS, PLUS}; 2 | use core::num::IntErrorKind::*; 3 | 4 | type PIE = ParseIntError2; 5 | 6 | pub fn parse_i16(s: &[u8]) -> Result { 7 | let mut s = s; //.as_bytes(); 8 | match s.get(0) { 9 | Some(val) => { 10 | let mut val = val.wrapping_sub(b'0'); 11 | if val > 9 { 12 | if val == MINUS { 13 | s = &s[1..]; 14 | val = match s.get(0) { 15 | Some(val) => { 16 | let val = val.wrapping_sub(b'0'); 17 | if val > 9 { 18 | return Err(PIE { kind: InvalidDigit }); 19 | }; 20 | val 21 | } 22 | None => return Err(PIE { kind: InvalidDigit }), 23 | }; 24 | let val2 = match s.get(1) { 25 | None => { 26 | return Ok(-(val as i16)); 27 | } 28 | Some(val2) => val2, 29 | }; 30 | let l = s.len(); 31 | return match l { 32 | 2 => { 33 | let val2 = val2.wrapping_sub(b'0'); 34 | if val2 <= 9 { 35 | return Ok(val as i16 * -10 - val2 as i16); 36 | } else { 37 | return Err(PIE { kind: InvalidDigit }); 38 | } 39 | } 40 | 3 => { 41 | let val2 = val2.wrapping_sub(b'0'); 42 | let val3 = s[2].wrapping_sub(b'0'); 43 | if (val2 <= 9) & (val3 <= 9) { 44 | Ok(val as i16 * -100 + val2 as i16 * -10 - val3 as i16) 45 | } else { 46 | return Err(PIE { kind: InvalidDigit }); 47 | } 48 | } 49 | 4 => parse_4_chars(s).map(|val| -(val as i16)), 50 | 5 => { 51 | if val <= 3 { 52 | let result = val as u16 * 10_000 + parse_4_chars(&s[1..])? as u16; 53 | if result <= 32767 { 54 | Ok(-(result as i16)) 55 | } else if result == 32768 { 56 | Ok(i16::MIN) 57 | } else { 58 | return Err(PIE { kind: PosOverflow }); 59 | } 60 | } else { 61 | return Err(PIE { kind: PosOverflow }); 62 | } 63 | } 64 | _ => Err(PIE { kind: PosOverflow }), 65 | }; 66 | } else if val == PLUS { 67 | s = &s[1..]; 68 | val = match s.get(0) { 69 | Some(val) => { 70 | let val = val.wrapping_sub(b'0'); 71 | if val > 9 { 72 | return Err(PIE { kind: InvalidDigit }); 73 | }; 74 | val 75 | } 76 | None => return Err(PIE { kind: InvalidDigit }), 77 | } 78 | } else { 79 | return Err(PIE { kind: InvalidDigit }); 80 | } 81 | } 82 | let val2 = match s.get(1) { 83 | None => { 84 | return Ok(val as i16); 85 | } 86 | Some(val2) => val2, 87 | }; 88 | 89 | let l = s.len(); 90 | match l { 91 | 2 => { 92 | let val2 = val2.wrapping_sub(b'0'); 93 | if val2 <= 9 { 94 | return Ok((val * 10 + val2) as i16); 95 | } else { 96 | return Err(PIE { kind: InvalidDigit }); 97 | } 98 | } 99 | 3 => { 100 | let val2 = val2.wrapping_sub(b'0'); 101 | let val3 = s[2].wrapping_sub(b'0'); 102 | if (val2 <= 9) & (val3 <= 9) { 103 | Ok(val as i16 * 100 + val2 as i16 * 10 + val3 as i16) 104 | } else { 105 | Err(PIE { kind: InvalidDigit }) 106 | } 107 | } 108 | 4 => parse_4_chars(s).map(|val| val as i16), 109 | 5 => { 110 | if val <= 3 { 111 | let result = val as u16 * 10_000 + parse_4_chars(&s[1..])? as u16; 112 | if result <= 32767 { 113 | Ok(result as i16) 114 | } else { 115 | Err(PIE { kind: PosOverflow }) 116 | } 117 | } else { 118 | Err(PIE { kind: PosOverflow }) 119 | } 120 | } 121 | _ => { 122 | let pos = s.iter().position(|byte| *byte != b'0'); 123 | if let Some(pos) = pos { 124 | if l - pos <= 5 { 125 | if s[pos] != b'+' { 126 | return parse_i16(&s[pos..]); 127 | } else { 128 | return Err(PIE { kind: InvalidDigit }); 129 | } 130 | } 131 | } else { 132 | return Ok(0); 133 | } 134 | return Err(PIE { kind: PosOverflow }); 135 | } 136 | } 137 | } 138 | None => return Err(PIE { kind: Empty }), 139 | } 140 | } 141 | 142 | // pub fn parse_i16(s: &str) -> Result { 143 | // let mut is_positive = 1; 144 | // let mut s = s.as_bytes(); 145 | // let (val, val2, val3) = match s.get(0) { 146 | // Some(val) => { 147 | // let mut val = val.wrapping_sub(b'0'); 148 | // if val > 9 { 149 | // if val == MINUS { 150 | // is_positive = -1; 151 | // s = &s[1..]; 152 | // val = match s.get(0) { 153 | // Some(val) => { 154 | // let val = val.wrapping_sub(b'0'); 155 | // if val > 9 { return Err(()) }; 156 | // val 157 | // }, 158 | // None => return Err(()), 159 | // } 160 | // } else if val == PLUS { 161 | // s = &s[1..]; 162 | // val = match s.get(0) { 163 | // Some(val) => { 164 | // let val = val.wrapping_sub(b'0'); 165 | // if val > 9 { return Err(()) }; 166 | // val 167 | // }, 168 | // None => return Err(()), 169 | // } 170 | // } else { return Err(()); } 171 | // } 172 | // let val2 = match s.get(1) { 173 | // None => { 174 | // return Ok(is_positive * val as i16); 175 | // }, 176 | // Some(val2) => { 177 | // val2 178 | // } 179 | // }; 180 | // let val3 = match s.get(2) { 181 | // None => { 182 | // let val2 = val2.wrapping_sub(b'0'); 183 | // if val2 > 9 { return Err(()); } 184 | // return Ok(is_positive * (val * 10 + val2) as i16); 185 | // }, 186 | // Some(val3) => { 187 | // val3 188 | // } 189 | // }; 190 | // (val, val2, val3) 191 | // } 192 | // None => return Err(()), 193 | // }; 194 | // let l = s.len(); 195 | // match l { 196 | // 3 => { 197 | // let val2 = val2.wrapping_sub(b'0'); 198 | // let val3 = val3.wrapping_sub(b'0'); 199 | // if (val2 > 9) | (val3 > 9) { return Err(()) } 200 | // Ok(is_positive * val as i16 * 100 + val2 as i16 * 10 + val3 as i16) 201 | // } 202 | // 4 => parse_4_chars(s).map(|val| is_positive * val as i16), 203 | // 5 => { 204 | // if val > 3 { return Err(()); } 205 | // let result = val as u16 * 10_000 + parse_4_chars(&s[1..])?; 206 | // if result <= 32767 { 207 | // Ok(is_positive * result as i16) 208 | // } 209 | // else if result == 32768 && is_positive == -1 { 210 | // Ok(i16::MIN) 211 | // } else { 212 | // return Err(()); 213 | // } 214 | // } 215 | // _ => Err(()), 216 | // } 217 | // } 218 | 219 | pub fn parse_i16_challenger(s: &[u8]) -> Result { 220 | let mut s = s; //.as_bytes(); 221 | let mut l = s.len(); 222 | match s.get(0) { 223 | Some(val) => { 224 | let mut val = val.wrapping_sub(b'0'); 225 | if val > 9 { 226 | if val == MINUS { 227 | val = match s.get(1) { 228 | Some(val) => { 229 | let val = val.wrapping_sub(b'0'); 230 | if val <= 9 { 231 | if l != 2 { 232 | val 233 | } else { 234 | return Ok(-(val as i16)); 235 | } 236 | } else { 237 | return Err(PIE { kind: InvalidDigit }); 238 | } 239 | } 240 | None => return Err(PIE { kind: InvalidDigit }), 241 | }; 242 | unsafe { 243 | return match l { 244 | 3 => { 245 | let val2 = s.get_unchecked(2); 246 | let val2 = val2.wrapping_sub(b'0'); 247 | if val2 <= 9 { 248 | return Ok(-((val * 10 + val2) as i16)); 249 | } else { 250 | return Err(PIE { kind: InvalidDigit }); 251 | } 252 | } 253 | 4 => { 254 | let val2 = s.get_unchecked(2); 255 | let val3 = s.get_unchecked(3); 256 | let val2 = val2.wrapping_sub(b'0'); 257 | let val3 = val3.wrapping_sub(b'0'); 258 | if (val2 <= 9) & (val3 <= 9) { 259 | Ok(-(val as i16 * 100 + val2 as i16 * 10 + val3 as i16)) 260 | } else { 261 | return Err(PIE { kind: InvalidDigit }); 262 | } 263 | } 264 | 5 => parse_4_chars(&s.get_unchecked(1..)).map(|val| -(val as i16)), 265 | 6 => { 266 | if val <= 3 { 267 | let result = val as u16 * 10_000 268 | + parse_4_chars(&s.get_unchecked(2..))? as u16; 269 | if result <= 32767 { 270 | Ok(-(result as i16)) 271 | } else if result == 32768 { 272 | Ok(i16::MIN) 273 | } else { 274 | return Err(PIE { kind: NegOverflow }); 275 | } 276 | } else { 277 | return Err(PIE { kind: NegOverflow }); 278 | } 279 | } 280 | _ => Err(PIE { kind: NegOverflow }), 281 | }; 282 | } 283 | } else if val == PLUS { 284 | s = &s[1..]; 285 | l -= 1; 286 | val = match s.get(0) { 287 | Some(val) => { 288 | let val = val.wrapping_sub(b'0'); 289 | if val > 9 { 290 | return Err(PIE { kind: InvalidDigit }); 291 | }; 292 | val 293 | } 294 | None => return Err(PIE { kind: InvalidDigit }), 295 | } 296 | } else { 297 | return Err(PIE { kind: InvalidDigit }); 298 | } 299 | } 300 | if l == 1 { 301 | return Ok(val as i16); 302 | }; 303 | match l { 304 | // 1 => { return Ok(val as i16); } 305 | 2 => unsafe { 306 | let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 307 | if val2 <= 9 { 308 | return Ok((val * 10 + val2) as i16); 309 | } else { 310 | return Err(PIE { kind: InvalidDigit }); 311 | } 312 | }, 313 | 3 => unsafe { 314 | let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 315 | let val3 = s.get_unchecked(2).wrapping_sub(b'0'); 316 | if (val2 <= 9) & (val3 <= 9) { 317 | Ok(val as i16 * 100 + val2 as i16 * 10 + val3 as i16) 318 | } else { 319 | Err(PIE { kind: InvalidDigit }) 320 | } 321 | }, 322 | 4 => { 323 | // Ok(0) 324 | parse_4_chars(s).map(|val| val as i16) 325 | } 326 | 5 => { 327 | if val <= 3 { 328 | let result = val as u16 * 10_000 + parse_4_chars(&s[1..])? as u16; 329 | if result <= 32767 { 330 | Ok(result as i16) 331 | } else { 332 | Err(PIE { kind: PosOverflow }) 333 | } 334 | } else { 335 | Err(PIE { kind: PosOverflow }) 336 | } 337 | } 338 | _ => { 339 | let pos = s.iter().position(|byte| *byte != b'0'); 340 | if let Some(pos) = pos { 341 | if l - pos <= 5 { 342 | if s[pos] != b'+' { 343 | return parse_i16(&s[pos..]); 344 | } else { 345 | return Err(PIE { kind: InvalidDigit }); 346 | } 347 | } 348 | } else { 349 | return Ok(0); 350 | } 351 | return Err(PIE { kind: PosOverflow }); 352 | } 353 | } 354 | } 355 | None => return Err(PIE { kind: Empty }), 356 | } 357 | } 358 | 359 | // pub fn parse_i16_old_best(s: &str) -> Result { 360 | // let mut is_positive = 1_i16; 361 | // let mut iter = s.as_bytes().iter(); 362 | // match iter.next() { 363 | // Some(mut val) => { 364 | // if *val == b'-' { 365 | // is_positive = -1; 366 | // match iter.next() { 367 | // Some(alt_val) => { 368 | // val = alt_val; 369 | // } 370 | // None => return Err(()), 371 | // } 372 | // } else if *val == b'+' { 373 | // match iter.next() { 374 | // Some(alt_val) => { 375 | // val = alt_val; 376 | // } 377 | // None => return Err(()), 378 | // } 379 | // } 380 | // let val = val.wrapping_sub(b'0'); 381 | // match iter.next() { 382 | // None => { 383 | // if val <= 9 { 384 | // Ok(is_positive * val as i16) 385 | // } else { 386 | // Err(()) 387 | // } 388 | // }, 389 | // Some(val2) => { 390 | // let val2 = val2.wrapping_sub(b'0'); 391 | // if val <= 9 && val2 <= 9 { 392 | // match iter.next() { 393 | // None => { 394 | // if val <= 9 { 395 | // Ok(is_positive * (val * 10 + val2) as i16) 396 | // } else { 397 | // Err(()) 398 | // } 399 | // } 400 | // Some(val3) => { 401 | // let val3 = val3.wrapping_sub(b'0'); 402 | // if val3 <= 9 { 403 | // match iter.next() { 404 | // None => { 405 | // let result = val as i16 * 100 + val2 as i16 * 10 + val3 as i16; 406 | // Ok(is_positive * result) 407 | // } 408 | // Some(val4) => { 409 | // let val4 = val4.wrapping_sub(b'0'); 410 | // if val4 <= 9 { 411 | // match iter.next() { 412 | // None => { 413 | // let result = val as i16 * 1000 + val2 as i16 * 100 + val3 as i16 * 10 + val4 as i16; 414 | // Ok(is_positive * result) 415 | // } 416 | // Some(val5) => { 417 | // match iter.next() { 418 | // None => { 419 | // let val5 = val5.wrapping_sub(b'0'); 420 | 421 | // if val5 <= 9 && val <= 3 { 422 | // let result = 10_000 * val as u16 423 | // + 1000 * val2 as u16 424 | // + 100 * val3 as u16 + val4 as u16 * 10 + val5 as u16; 425 | // if result <= 32767 { 426 | // Ok(is_positive * result as i16) 427 | // } 428 | // else if result == 32768 && is_positive == -1 { 429 | // Ok(i16::MIN) 430 | // } else { 431 | // return Err(()); 432 | // } 433 | // } else {return Err(())} 434 | // } 435 | // Some(_too_many_digits) => { Err(()) } 436 | // } 437 | // } 438 | // } 439 | // } else { return Err(()) } 440 | // } 441 | // } 442 | // } else { return Err(()) } 443 | // } 444 | // } 445 | // } else {return Err(());} 446 | // } 447 | // } 448 | // } 449 | // _ => Err(()), 450 | // } 451 | // } 452 | -------------------------------------------------------------------------------- /src/parse_i8.rs: -------------------------------------------------------------------------------- 1 | use super::{parse, ParseIntError2, PLUS}; 2 | use core::num::IntErrorKind::*; 3 | 4 | type PIE = ParseIntError2; 5 | 6 | pub fn parse_i8(s: &[u8]) -> Result { 7 | let mut iter = s.iter(); 8 | match iter.next() { 9 | Some(val) => { 10 | if *val == b'-' { 11 | return match iter.next() { 12 | Some(val) => { 13 | let mut val = val.wrapping_sub(b'0'); 14 | if val <= 9 { 15 | while val == 0 { 16 | val = match iter.next() { 17 | Some(val2) => val2.wrapping_sub(b'0'), 18 | None => { 19 | return Ok(0); 20 | } 21 | } 22 | } 23 | match iter.next() { 24 | None => Ok(-(val as i8)), 25 | Some(val2) => { 26 | let val2 = val2.wrapping_sub(b'0'); 27 | if val2 <= 9 { 28 | match iter.next() { 29 | None => Ok(-((val * 10 + val2) as i8)), 30 | Some(val3) => match iter.next() { 31 | None => { 32 | let val3 = val3.wrapping_sub(b'0'); 33 | if (val3 <= 9) & (val <= 1) { 34 | let result = val * 100 + val2 * 10 + val3; 35 | if result < 128 { 36 | Ok(-(result as i8)) 37 | } else if result == 128 { 38 | Ok(i8::MIN) 39 | } else { 40 | Err(PIE { kind: NegOverflow }) 41 | } 42 | } else { 43 | Err(PIE { kind: NegOverflow }) 44 | } 45 | } 46 | Some(_) => return Err(PIE { kind: PosOverflow }), 47 | }, 48 | } 49 | } else { 50 | Err(PIE { kind: InvalidDigit }) 51 | } 52 | } 53 | } 54 | } else { 55 | Err(PIE { kind: InvalidDigit }) 56 | } 57 | } 58 | _ => Err(PIE { kind: InvalidDigit }), 59 | }; 60 | } 61 | let mut val = val.wrapping_sub(b'0'); 62 | if val > 9 { 63 | if val == PLUS { 64 | val = match iter.next() { 65 | Some(val) => { 66 | let val = val.wrapping_sub(b'0'); 67 | if val <= 9 { 68 | val 69 | } else { 70 | return Err(PIE { kind: InvalidDigit }); 71 | } 72 | } 73 | None => return Err(PIE { kind: InvalidDigit }), 74 | } 75 | } else { 76 | return Err(PIE { kind: InvalidDigit }); 77 | } 78 | } 79 | while val == 0 { 80 | val = match iter.next() { 81 | Some(val2) => val2.wrapping_sub(b'0'), 82 | None => { 83 | return Ok(0); 84 | } 85 | } 86 | } 87 | match iter.next() { 88 | None => Ok(val as i8), 89 | Some(val2) => { 90 | let val2 = val2.wrapping_sub(b'0'); 91 | if val2 <= 9 { 92 | match iter.next() { 93 | None => Ok((val * 10 + val2) as i8), 94 | Some(val3) => match iter.next() { 95 | None => { 96 | let val3 = val3.wrapping_sub(b'0'); 97 | if (val3 <= 9) & (val <= 1) { 98 | let result = val * 100 + val2 * 10 + val3; 99 | if result < 128 { 100 | Ok(result as i8) 101 | } else { 102 | Err(PIE { kind: PosOverflow }) 103 | } 104 | } else { 105 | return Err(PIE { kind: PosOverflow }); 106 | } 107 | } 108 | Some(_) => return Err(PIE { kind: PosOverflow }), 109 | }, 110 | } 111 | } else { 112 | return Err(PIE { kind: InvalidDigit }); 113 | } 114 | } 115 | } 116 | } 117 | _ => Err(PIE { kind: Empty }), 118 | } 119 | } 120 | 121 | // pub fn parse_i8_old_old(s: &str) -> Result { 122 | // let mut is_positive = 1; 123 | // let mut iter = s.as_bytes().iter(); 124 | // match iter.next() { 125 | // Some(mut val) => { 126 | // if *val == b'-' { 127 | // is_positive = -1; 128 | // match iter.next() { 129 | // Some(alt_val) => { 130 | // val = alt_val; 131 | // } 132 | // None => return Err(()), 133 | // } 134 | // } else if *val == b'+' { 135 | // match iter.next() { 136 | // Some(alt_val) => { 137 | // val = alt_val; 138 | // } 139 | // None => return Err(()), 140 | // } 141 | // } 142 | // let val = val.wrapping_sub(b'0'); 143 | // match iter.next() { 144 | // None => { 145 | // if val <= 9 { 146 | // Ok(is_positive * val as i8) 147 | // } else { 148 | // Err(()) 149 | // } 150 | // }, 151 | // Some(val2) => { 152 | // let val2 = val2.wrapping_sub(b'0'); 153 | // if val2 <= 9 { 154 | // match iter.next() { 155 | // None => { 156 | // if val <= 9 { 157 | // Ok(is_positive * (val * 10 + val2) as i8) 158 | // } else { 159 | // Err(()) 160 | // } 161 | // } 162 | // Some(val3) => { 163 | // let val3 = val3.wrapping_sub(b'0'); 164 | // let val2 = val2 * 10; 165 | // if (val3 <= 9) & (val <= 1) { 166 | // let result = val * 100 + val2 + val3; 167 | // if result < 128 { 168 | // Ok(is_positive * (result as i8)) 169 | // } else if result == 128 && is_positive == -1 { 170 | // Ok(i8::MIN) 171 | // } else { 172 | // Err(()) 173 | // } 174 | // // match val { 175 | // // 1 => { 176 | // // let result = 100 + val2 + val3; 177 | // // if result < 128 { 178 | // // Ok(is_positive * (result as i8)) 179 | // // } else if result == 128 && is_positive == -1 { 180 | // // Ok(i8::MIN) 181 | // // } else { 182 | // // Err(()) 183 | // // } 184 | // // }, 185 | // // 0 => { 186 | // // Ok(is_positive * ((val2 + val3) as i8)) 187 | // // }, 188 | // // _ => Err(()), 189 | // // } 190 | // } else { return Err(()) } 191 | // } 192 | // } 193 | // } else {return Err(());} 194 | // } 195 | // } 196 | // } 197 | // _ => Err(()), 198 | // } 199 | // } 200 | 201 | pub fn parse_i8_challenger(s: &[u8]) -> Result { 202 | parse::(s).map_err(|_| PIE { kind: InvalidDigit }) 203 | } 204 | 205 | // pub fn parse_i8_challenger(s: &[u8]) -> Result { 206 | // let mut iter = s.iter(); 207 | // match iter.next() { 208 | // Some(mut val) => { 209 | // if *val == b'-' { 210 | // return match iter.next() { 211 | // Some(val) => { 212 | // let val = val.wrapping_sub(b'0'); 213 | // if val <= 9 { 214 | // match iter.next() { 215 | // None => Ok(-(val as i8)), 216 | // Some(val2) => { 217 | // let val2 = val2.wrapping_sub(b'0'); 218 | // if val2 <= 9 { 219 | // match iter.next() { 220 | // None => Ok(-((val * 10 + val2) as i8)), 221 | // Some(val3) => match iter.next() { 222 | // None => { 223 | // let val3 = val3.wrapping_sub(b'0'); 224 | // if (val3 <= 9) & (val <= 1) { 225 | // let result = val * 100 + val2 * 10 + val3; 226 | // if result < 128 { 227 | // Ok(-(result as i8)) 228 | // } else if result == 128 { 229 | // Ok(i8::MIN) 230 | // } else { 231 | // Err(PIE { kind: PosOverflow }) 232 | // } 233 | // } else { 234 | // Err(PIE { kind: PosOverflow }) 235 | // } 236 | // } 237 | // Some(_) => return Err(PIE { kind: PosOverflow }), 238 | // }, 239 | // } 240 | // } else { 241 | // Err(PIE { kind: InvalidDigit }) 242 | // } 243 | // } 244 | // } 245 | // } else { 246 | // Err(PIE { kind: InvalidDigit }) 247 | // } 248 | // } 249 | // _ => Err(PIE { kind: InvalidDigit }), 250 | // }; 251 | // } else if *val == b'+' { 252 | // match iter.next() { 253 | // Some(alt_val) => { 254 | // val = alt_val; 255 | // } 256 | // None => return Err(PIE { kind: InvalidDigit }), 257 | // } 258 | // } 259 | // let val = val.wrapping_sub(b'0'); 260 | // match iter.next() { 261 | // None => { 262 | // if val <= 9 { 263 | // Ok(val as i8) 264 | // } else { 265 | // Err(PIE { kind: InvalidDigit }) 266 | // } 267 | // } 268 | // Some(val2) => { 269 | // let val2 = val2.wrapping_sub(b'0'); 270 | // if val2 <= 9 { 271 | // match iter.next() { 272 | // None => { 273 | // if val <= 9 { 274 | // Ok((val * 10 + val2) as i8) 275 | // } else { 276 | // Err(PIE { kind: InvalidDigit }) 277 | // } 278 | // } 279 | // Some(val3) => match iter.next() { 280 | // None => { 281 | // let val3 = val3.wrapping_sub(b'0'); 282 | // let val2 = val2 * 10; 283 | // if (val3 <= 9) & (val <= 1) { 284 | // let result = val * 100 + val2 + val3; 285 | // if result < 128 { 286 | // Ok(result as i8) 287 | // } else { 288 | // Err(PIE { kind: PosOverflow }) 289 | // } 290 | // } else { 291 | // return Err(PIE { kind: PosOverflow }); 292 | // } 293 | // } 294 | // Some(_) => return Err(PIE { kind: PosOverflow }), 295 | // }, 296 | // } 297 | // } else { 298 | // return Err(PIE { kind: InvalidDigit }); 299 | // } 300 | // } 301 | // } 302 | // } 303 | // _ => Err(PIE { kind: Empty }), 304 | // } 305 | // } 306 | 307 | // pub fn parse_i8_old_best(s: &str) -> Result { 308 | // let mut is_positive = 1; 309 | // let mut iter = s.as_bytes().iter(); 310 | // match iter.next() { 311 | // Some(mut val) => { 312 | // if *val == b'-' { 313 | // is_positive = -1; 314 | // match iter.next() { 315 | // Some(alt_val) => { 316 | // val = alt_val; 317 | // } 318 | // None => return Err(()), 319 | // } 320 | // } else if *val == b'+' { 321 | // match iter.next() { 322 | // Some(alt_val) => { 323 | // val = alt_val; 324 | // } 325 | // None => return Err(()), 326 | // } 327 | // } 328 | // let val = val.wrapping_sub(b'0'); 329 | // match iter.next() { 330 | // None => { 331 | // if val <= 9 { 332 | // Ok(is_positive * val as i8) 333 | // } else { 334 | // Err(()) 335 | // } 336 | // }, 337 | // Some(val2) => { 338 | // let val2 = val2.wrapping_sub(b'0'); 339 | // if val2 <= 9 { 340 | // match iter.next() { 341 | // None => { 342 | // if val <= 9 { 343 | // Ok(is_positive * (val * 10 + val2) as i8) 344 | // } else { 345 | // Err(()) 346 | // } 347 | // } 348 | // Some(val3) => { 349 | // let val3 = val3.wrapping_sub(b'0'); 350 | // let val2 = val2 * 10; 351 | // if val3 <= 9 { 352 | // match val { 353 | // 1 => { 354 | // let result = 100 + val2 + val3; 355 | // if result < 128 { 356 | // Ok(is_positive * (result as i8)) 357 | // } else if result == 128 && is_positive == -1 { 358 | // Ok(i8::MIN) 359 | // } else { 360 | // Err(()) 361 | // } 362 | // }, 363 | // 0 => { 364 | // Ok(is_positive * ((val2 + val3) as i8)) 365 | // }, 366 | // _ => Err(()), 367 | // } 368 | // } else { return Err(()) } 369 | // } 370 | // } 371 | // } else {return Err(());} 372 | // } 373 | // } 374 | // } 375 | // _ => Err(()), 376 | // } 377 | // } 378 | 379 | // pub fn parse_i8_best_old1(s: &str) -> Result { 380 | // let mut is_positive = 1;//tODO try * -1 / 1 381 | // let mut iter = s.as_bytes().iter(); 382 | // match iter.next() { 383 | // Some(mut val) => { 384 | // if *val == b'+' { 385 | // match iter.next() { 386 | // Some(alt_val) => { 387 | // val = alt_val; 388 | // } 389 | // None => return Err(()), 390 | // } 391 | // } else if *val == b'-' { 392 | // is_positive = -1; 393 | // match iter.next() { 394 | // Some(alt_val) => { 395 | // val = alt_val; 396 | // } 397 | // None => return Err(()), 398 | // } 399 | // } 400 | // let val = val.wrapping_sub(b'0'); 401 | // match iter.next() { 402 | // Some(val2) => { 403 | // let val2 = val2.wrapping_sub(b'0'); 404 | // if val2 > 9 { 405 | // return Err(()); 406 | // } 407 | // match iter.next() { 408 | // Some(val3) => { 409 | // let val3 = val3.wrapping_sub(b'0'); 410 | // let val2 = val2 * 10; 411 | // match val { 412 | // 1 => { 413 | // let result = 100 + val2 + val3; 414 | // if is_positive == 1 { 415 | // if result <= i8::MAX as u8 { 416 | // Ok(result as i8) 417 | // } else { 418 | // Err(()) 419 | // } 420 | // } else { 421 | // if result <= 128 { 422 | // Ok(-(result as i8)) 423 | // } else { 424 | // Err(()) 425 | // } 426 | // } 427 | // }, 428 | // 0 => { 429 | // Ok(is_positive * ((val2 + val3) as i8)) 430 | // }, 431 | // _ => Err(()), 432 | // } 433 | // } 434 | // None => { 435 | // if val <= 9 { 436 | // Ok(is_positive * (val * 10 + val2) as i8) 437 | // } else { 438 | // Err(()) 439 | // } 440 | // } 441 | // } 442 | // } 443 | // None => { 444 | // if val <= 9 { 445 | // Ok(is_positive * val as i8) 446 | // } else { 447 | // Err(()) 448 | // } 449 | // } 450 | // } 451 | // } 452 | // _ => Err(()), 453 | // } 454 | // } 455 | 456 | // pub fn parse_i8_best_old(src: &str) -> Result { 457 | // let (is_positive, digits) = match src.as_bytes().get(0) { 458 | // None => { 459 | // return Err(()); 460 | // } 461 | // Some(b'-') => (false, &src[1..]), 462 | // Some(_) => (true, src), 463 | // }; 464 | // let i = parse_u8(digits)?; 465 | // if is_positive { 466 | // if i > i8::MAX as u8 { 467 | // Err(()) 468 | // } else { 469 | // Ok(i as i8) 470 | // } 471 | // } else { 472 | // // Negative 473 | // if i > i8::MAX as u8 + 1 { 474 | // Err(()) 475 | // } else { 476 | // match 0_i8.checked_sub(i as i8) { 477 | // Some(res) => Ok(res), 478 | // None => Err(()), 479 | // } 480 | // } 481 | // } 482 | // } 483 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | parse_16_chars, parse_2_chars, parse_32_chars, parse_4_chars, parse_8_chars, trees::*, 3 | IntErrorKind, ParseIntErrorPublic, MINUS, PLUS, 4 | }; 5 | use core::ops::{Add, Mul, Sub}; 6 | 7 | macro_rules! likely { 8 | ($e:expr) => { 9 | $e 10 | //TODO: use core::intrinsics::likely($e) when stable 11 | }; 12 | } 13 | 14 | macro_rules! unlikely { 15 | ($e:expr) => { 16 | $e 17 | //TODO: use core::intrinsics::unlikely($e) when stable 18 | }; 19 | } 20 | 21 | type Pie = ParseIntErrorPublic; 22 | 23 | #[doc(hidden)] 24 | pub trait FromStrRadixHelper: 25 | PartialOrd + Copy + Add + Mul + Sub + 'static 26 | { 27 | const MIN: Self; 28 | const BITS: u32; 29 | const FIRST_SIG: u8; 30 | const TAIL: Self; 31 | const TREE: &'static [Self]; 32 | const CHARS: usize; 33 | fn from_u128(u: u128) -> Self; 34 | fn from_u64(u: u64) -> Self; 35 | fn from_u32(u: u32) -> Self; 36 | fn from_u16(u: u16) -> Self; 37 | fn from_u8(u: u8) -> Self; 38 | 39 | fn mul_checked(&self, other: Self) -> Option; 40 | fn sub_checked(&self, other: Self) -> Option; 41 | fn add_checked(&self, other: Self) -> Option; 42 | } 43 | 44 | macro_rules! doit { 45 | ($($t:ty,$tr:expr,$chars:literal,$first_sig:literal,$tail:literal)*) => ($(impl FromStrRadixHelper for $t { 46 | const MIN: Self = Self::MIN; 47 | const FIRST_SIG: u8 = $first_sig; 48 | const TAIL: Self = $tail; 49 | const BITS: u32 = Self::BITS; 50 | const TREE: &'static[Self] = $tr; 51 | const CHARS: usize = $chars; 52 | #[inline(always)] 53 | fn from_u128(u: u128) -> Self { u as Self } 54 | #[inline(always)] 55 | fn from_u64(u: u64) -> Self { u as Self } 56 | #[inline(always)] 57 | fn from_u32(u: u32) -> Self { u as Self } 58 | #[inline(always)] 59 | fn from_u16(u: u16) -> Self { u as Self } 60 | #[inline(always)] 61 | fn from_u8(u: u8) -> Self { u as Self } 62 | #[inline(always)] 63 | fn mul_checked(&self, other: Self) -> Option { 64 | Self::checked_mul(*self, other as Self) 65 | } 66 | #[inline(always)] 67 | fn sub_checked(&self, other: Self) -> Option { 68 | Self::checked_sub(*self, other as Self) 69 | } 70 | #[inline(always)] 71 | fn add_checked(&self, other: Self) -> Option { 72 | Self::checked_add(*self, other as Self) 73 | } 74 | })*) 75 | } 76 | 77 | doit! { 78 | i8,TENS_I8,3,1,-28 79 | i16,TENS_I16,5,3,-2768 80 | i32,TENS_I32,10,2,-147_483_648 81 | i64,TENS_I64,19,9,-223_372_036_854_775_808 82 | i128,TENS_I128,39,1,-70141183460469231731687303715884105728 83 | u8,TENS_U8,3,2,55 84 | u16,TENS_U16,5,6,5535 85 | u32,TENS_U32,10,4,294_967_295 86 | u64,TENS_U64,20,1,8_446_744_073_709_551_615 87 | u128,TENS_U128,39,3,40_282_366_920_938_463_463_374_607_431_768_211_455 88 | } 89 | 90 | #[cfg(target_pointer_width = "16")] //E.g. msp430-none-elf micro-controller. 91 | doit! { 92 | isize,TENS_ISIZE,5,3,-2768 93 | usize,TENS_USIZE,5,6,5535 94 | } 95 | 96 | #[cfg(target_pointer_width = "32")] 97 | doit! { 98 | isize,TENS_ISIZE,10,2,-147_483_648 99 | usize,TENS_USIZE,10,4,294_967_295 100 | } 101 | 102 | #[cfg(target_pointer_width = "64")] 103 | doit! { 104 | isize,TENS_ISIZE,19,9,-223_372_036_854_775_808 105 | usize,TENS_USIZE,20,1,8_446_744_073_709_551_615 106 | } 107 | 108 | // u128: 0 to 340_282_366_920_938_463_463_374_607_431_768_211_455 109 | // (39 digits!) 110 | #[doc(hidden)] 111 | pub fn parse_challenger(s: &[u8]) -> Result 112 | where 113 | T: FromStrRadixHelper, 114 | { 115 | parse(s) 116 | } 117 | 118 | // u128: 0 to 340_282_366_920_938_463_463_374_607_431_768_211_455 119 | // (39 digits!) 120 | 121 | macro_rules! invalid { 122 | () => { 123 | Pie { 124 | kind: IntErrorKind::InvalidDigit, 125 | } 126 | }; 127 | } 128 | macro_rules! empty { 129 | () => { 130 | Pie { 131 | kind: IntErrorKind::Empty, 132 | } 133 | }; 134 | } 135 | 136 | macro_rules! pos_overflow { 137 | () => { 138 | Pie { 139 | kind: IntErrorKind::PosOverflow, 140 | } 141 | }; 142 | } 143 | 144 | macro_rules! neg_overflow { 145 | () => { 146 | Pie { 147 | kind: IntErrorKind::NegOverflow, 148 | } 149 | }; 150 | } 151 | 152 | /// Parses a UTF8 &str as a number. 153 | /// 154 | /// It has exactly the same semantics as `std::str::parse`, 155 | /// but faster. (compiled with nightly,simd features 156 | /// and target native cpu will get the absolute fastest result.) 157 | /// 158 | /// Positives are slightly faster than negatives when parsing and 159 | /// if you don't need to put a leading `+` then that will be faster too. 160 | /// # Examples 161 | /// 162 | /// ```rust 163 | /// let s = "+000123"; 164 | /// assert_eq!(atoi_radix10::parse_from_str(s), Ok(123)); 165 | /// ``` 166 | #[inline(always)] 167 | pub fn parse_from_str>(s: S) -> Result { 168 | parse(s.as_ref().as_bytes()) 169 | } 170 | 171 | /// Parses a UTF8 &[u8] slice as a number. 172 | /// 173 | /// Takes a `&[u8]` because any non-utf/non-ascii will fail parsing as an integer. 174 | /// 175 | /// It has exactly the same semantics as `std::str::parse`, 176 | /// but faster. (compiled with nightly,simd features 177 | /// and target native cpu will get the absolute fastest result.) 178 | /// 179 | /// Positives are slightly faster than negatives when parsing and 180 | /// if you don't need to put a leading `+` then that will be faster too. 181 | /// 182 | /// # Examples 183 | /// 184 | /// ```rust 185 | /// let s: String = "+000123".into(); 186 | /// assert_eq!(atoi_radix10::parse::(s.as_bytes()), Ok(123)); 187 | /// ``` 188 | pub fn parse(mut s: &[u8]) -> Result 189 | where 190 | T: FromStrRadixHelper, 191 | { 192 | let is_signed_ty = T::from_u32(0) > T::MIN; 193 | let mut checked: Option<(u8, T)> = None; 194 | if let Some(val) = s.first() { 195 | let mut val = val.wrapping_sub(b'0'); 196 | loop { 197 | if likely!(val <= 9) { 198 | // positive without +. could be long with lots of leading zeros. 199 | loop { 200 | let l = s.len(); 201 | if likely!(l < T::CHARS) { 202 | let mut res = T::from_u8(0); 203 | 204 | for _ in 0..1 { 205 | // Align so that s ptr ends b0 206 | if s.as_ptr() as usize & 1 != 0 { 207 | let val_t = T::from_u8(val); 208 | s = &s[1..]; 209 | if s.is_empty() { 210 | res = val_t; 211 | break; 212 | } 213 | res = val_t * T::TREE[s.len()]; 214 | } 215 | if s.len() >= 2 && T::BITS >= 8 { 216 | // Align so that s ptr ends b00 217 | if s.as_ptr() as usize & 2 != 0 { 218 | let val = T::from_u16(unsafe { parse_2_chars(s) }?); 219 | s = &s[2..]; 220 | if s.is_empty() { 221 | res = res + val; 222 | break; 223 | } 224 | res = res + T::TREE[s.len()] * val; 225 | } 226 | if s.len() >= 4 && T::BITS >= 16 { 227 | // Align so that s ptr ends b000 228 | if s.as_ptr() as usize & 4 != 0 { 229 | let val = T::from_u16(unsafe { parse_4_chars(s) }?); 230 | s = &s[4..]; 231 | if s.is_empty() { 232 | res = res + val; 233 | break; 234 | } 235 | res = res + T::TREE[s.len()] * val; 236 | } 237 | if s.len() >= 8 && T::BITS >= 32 { 238 | // Align so that s ptr ends b0000 239 | if s.as_ptr() as usize & 8 != 0 { 240 | let val = T::from_u32(unsafe { parse_8_chars(s) }?); 241 | s = &s[8..]; 242 | if s.is_empty() { 243 | res = res + val; 244 | break; 245 | } 246 | res = res + T::TREE[s.len()] * val; 247 | } 248 | if s.len() >= 16 && T::BITS >= 64 { 249 | // Align so that s ptr ends b00000 250 | if s.as_ptr() as usize & 16 != 0 { 251 | let val = 252 | T::from_u64(unsafe { parse_16_chars(s) }?); 253 | s = &s[16..]; 254 | if s.is_empty() { 255 | res = res + val; 256 | break; 257 | } 258 | res = res + T::TREE[s.len()] * val; 259 | } 260 | 261 | // Did you see what we did there? at this point, 262 | // s is aligned for reading as a u128. 263 | if s.len() >= 32 && T::BITS >= 128 { 264 | let val = 265 | T::from_u128(unsafe { parse_32_chars(s) }?); 266 | s = &s[32..]; 267 | if s.is_empty() { 268 | res = res + val; 269 | break; 270 | } 271 | res = res + T::TREE[s.len()] * val; 272 | } 273 | 274 | //Even if we couldn't take 32 chars, 16 chars is aligned 275 | if s.len() >= 16 { 276 | let val = 277 | T::from_u64(unsafe { parse_16_chars(s) }?); 278 | s = &s[16..]; 279 | if s.is_empty() { 280 | res = res + val; 281 | break; 282 | } 283 | res = res + T::TREE[s.len()] * val; 284 | } 285 | } 286 | 287 | if s.len() >= 8 { 288 | let val = T::from_u32(unsafe { parse_8_chars(s) }?); 289 | s = &s[8..]; 290 | if s.is_empty() { 291 | res = res + val; 292 | break; 293 | } 294 | res = res + T::TREE[s.len()] * val; 295 | } 296 | } 297 | 298 | if s.len() >= 4 { 299 | let val = T::from_u16(unsafe { parse_4_chars(s) }?); 300 | s = &s[4..]; 301 | if s.is_empty() { 302 | res = res + val; 303 | break; 304 | } 305 | res = res + T::TREE[s.len()] * val; 306 | } 307 | } 308 | 309 | if s.len() >= 2 { 310 | let val = T::from_u16(unsafe { parse_2_chars(s) }?); 311 | s = &s[2..]; 312 | if s.is_empty() { 313 | res = res + val; 314 | break; 315 | } 316 | res = res + T::TREE[s.len()] * val; 317 | } 318 | } 319 | 320 | if let Some(val) = s.first() { 321 | let val = val.wrapping_sub(b'0'); 322 | if val > 9 { 323 | return Err(invalid!()); 324 | } 325 | res = res + T::from_u8(val); 326 | } 327 | } 328 | return if let Some((_, checked_t)) = checked { 329 | checked_t.add_checked(res).ok_or(pos_overflow!()) 330 | } else { 331 | Ok(res) 332 | }; 333 | } 334 | // Deal with edge cases then get back to the top, 335 | if l == T::CHARS && val <= T::FIRST_SIG { 336 | // SAFETY: mul is in range as `checked` is constrained to <= T::FIRST_SIG 337 | let v = T::from_u8(val); 338 | let mult = v * T::TREE[T::CHARS - 1]; 339 | let val_next = s[1]; 340 | checked = Some((val, mult)); 341 | val = val_next.wrapping_sub(b'0'); 342 | s = &s[1..]; 343 | if unlikely!(val > 9) { 344 | return Err(invalid!()); 345 | } 346 | } else if val == 0 { 347 | // Remove leading zeros 348 | val = b'0'; 349 | while val == b'0' { 350 | s = &s[1..]; 351 | val = match s.first() { 352 | Some(val) => *val, 353 | None => return Ok(T::from_u8(0)), 354 | } 355 | } 356 | val = val.wrapping_sub(b'0'); 357 | if val > 9 { 358 | return Err(empty!()); 359 | } 360 | } else { 361 | return Err(pos_overflow!()); 362 | } 363 | debug_assert!(val <= 9); 364 | } 365 | } else if likely!(is_signed_ty && val == MINUS) { 366 | s = &s[1..]; 367 | 368 | // negative without -. could be long with lots of leading zeros. 369 | loop { 370 | let l = s.len(); 371 | if likely!(l < T::CHARS && l != 0) { 372 | let mut res = T::from_u8(0); 373 | for _ in 0..1 { 374 | // Align so that s ptr ends b0 375 | if s.as_ptr() as usize & 1 != 0 { 376 | let val = s[0].wrapping_sub(b'0'); 377 | if likely!(val <= 9 && l == 1) { 378 | res = res - T::from_u8(val); 379 | return Ok(res); 380 | } else if likely!(val <= 9) { 381 | res = res - T::from_u8(val); 382 | s = &s[1..]; 383 | res = res * T::TREE[s.len()]; 384 | } else { 385 | return Err(invalid!()); 386 | }; 387 | } 388 | if s.len() >= 2 && T::BITS >= 8 { 389 | // Align so that s ptr ends b00 390 | if s.as_ptr() as usize & 2 != 0 { 391 | let val = unsafe { T::from_u16(parse_2_chars(s)?) }; 392 | s = &s[2..]; 393 | if s.is_empty() { 394 | res = res - val; 395 | break; 396 | } 397 | res = res - T::TREE[s.len()] * val; 398 | } 399 | if s.len() >= 4 && T::BITS >= 16 { 400 | // Align so that s ptr ends b000 401 | if s.as_ptr() as usize & 4 != 0 { 402 | let val = unsafe { T::from_u16(parse_4_chars(s)?) }; 403 | s = &s[4..]; 404 | if s.is_empty() { 405 | res = res - val; 406 | break; 407 | } 408 | res = res - T::TREE[s.len()] * val; 409 | } 410 | if s.len() >= 8 && T::BITS >= 32 { 411 | // Align so that s ptr ends b0000 412 | if s.as_ptr() as usize & 8 != 0 { 413 | let val = unsafe { T::from_u32(parse_8_chars(s)?) }; 414 | s = &s[8..]; 415 | if s.is_empty() { 416 | res = res - val; 417 | break; 418 | } 419 | res = res - T::TREE[s.len()] * val; 420 | } 421 | if s.len() >= 16 && T::BITS >= 64 { 422 | // Align so that s ptr ends b00000 423 | if s.as_ptr() as usize & 16 != 0 { 424 | let val = 425 | unsafe { T::from_u64(parse_16_chars(s)?) }; 426 | s = &s[16..]; 427 | if s.is_empty() { 428 | res = res - val; 429 | break; 430 | } 431 | res = res - T::TREE[s.len()] * val; 432 | } 433 | 434 | // s is aligned so that we can read u128 435 | if s.len() >= 32 && T::BITS >= 128 { 436 | let val = 437 | T::from_u128(unsafe { parse_32_chars(s) }?); 438 | s = &s[32..]; 439 | if s.is_empty() { 440 | res = res - val; 441 | break; 442 | } 443 | res = res - T::TREE[s.len()] * val; 444 | } 445 | // all the following are now aligned. 446 | if s.len() >= 16 { 447 | let val = 448 | T::from_u64(unsafe { parse_16_chars(s)? }); 449 | s = &s[16..]; 450 | if s.is_empty() { 451 | res = res - val; 452 | break; 453 | } 454 | res = res - T::TREE[s.len()] * val; 455 | } 456 | } 457 | if s.len() >= 8 { 458 | let val = T::from_u32(unsafe { parse_8_chars(s) }?); 459 | s = &s[8..]; 460 | if s.is_empty() { 461 | res = res - val; 462 | break; 463 | } 464 | res = res - T::TREE[s.len()] * val; 465 | } 466 | } 467 | if s.len() >= 4 { 468 | let val = T::from_u16(unsafe { parse_4_chars(s) }?); 469 | s = &s[4..]; 470 | if s.is_empty() { 471 | res = res - val; 472 | break; 473 | } 474 | res = res - T::TREE[s.len()] * val; 475 | } 476 | } 477 | if s.len() >= 2 { 478 | let val = T::from_u16(unsafe { parse_2_chars(s) }?); 479 | s = &s[2..]; 480 | if s.is_empty() { 481 | res = res - val; 482 | break; 483 | } 484 | res = res - T::TREE[s.len()] * val; 485 | } 486 | } 487 | if let Some(val) = s.first() { 488 | let val = val.wrapping_sub(b'0'); 489 | if unlikely!(val > 9) { 490 | return Err(invalid!()); 491 | }; 492 | res = res - T::from_u8(val); 493 | } 494 | } 495 | return if let Some((chk, chk_t)) = checked { 496 | if unlikely!(res == T::TAIL && chk == T::FIRST_SIG) { 497 | return Ok(T::MIN); 498 | } 499 | res.sub_checked(chk_t).ok_or(neg_overflow!()) 500 | } else { 501 | Ok(res) 502 | }; 503 | } 504 | val = if let Some(val) = s.first() { 505 | *val 506 | } else { 507 | return Err(empty!()); 508 | }; 509 | val = val.wrapping_sub(b'0'); 510 | if l == T::CHARS && val <= T::FIRST_SIG { 511 | // SAFETY: mul is in range as `checked` is constrained to <= T::FIRST_SIG 512 | checked = Some((val, T::from_u8(val) * T::TREE[T::CHARS - 1])); 513 | s = &s[1..]; 514 | } else if val == 0 { 515 | val = b'0'; 516 | while val == b'0' { 517 | s = &s[1..]; 518 | val = match s.first() { 519 | Some(val) => *val, 520 | None => return Ok(T::from_u8(0)), 521 | } 522 | } 523 | } else { 524 | return Err(neg_overflow!()); 525 | } 526 | } 527 | } else if val == PLUS { 528 | s = &s[1..]; 529 | val = match s.first() { 530 | Some(value) => { 531 | let value = value.wrapping_sub(b'0'); 532 | if likely!(value <= 9) { 533 | value 534 | } else { 535 | return Err(empty!()); 536 | } 537 | } 538 | None => return Err(empty!()), 539 | }; 540 | } else { 541 | return Err(invalid!()); 542 | } 543 | } 544 | } else { 545 | Err(empty!()) 546 | } 547 | } 548 | -------------------------------------------------------------------------------- /src/parse_u32.rs: -------------------------------------------------------------------------------- 1 | use super::{parse_2_chars, parse_4_chars, parse_8_chars, ParseIntError2, PLUS}; 2 | use core::num::IntErrorKind::*; 3 | 4 | type PIE = ParseIntError2; 5 | 6 | /// Parses from 0 -> 4_294_967_295 (10 digits and optionally +) 7 | pub fn parse_u32(mut s: &[u8]) -> Result { 8 | // let mut s = s.as_bytes(); 9 | let val = match s.get(0) { 10 | Some(val) => { 11 | let val = val.wrapping_sub(b'0'); 12 | if val <= 9 { 13 | val 14 | } else { 15 | if val == PLUS { 16 | s = &s[1..]; 17 | match s.get(0) { 18 | Some(val2) => { 19 | let val2 = (*val2).wrapping_sub(b'0'); 20 | if val2 <= 9 { 21 | val2 22 | } else { 23 | return Err(PIE { kind: InvalidDigit }); 24 | } 25 | } 26 | None => return Err(PIE { kind: InvalidDigit }), 27 | } 28 | } else { 29 | return Err(PIE { kind: InvalidDigit }); 30 | } 31 | } 32 | } 33 | None => return Err(PIE { kind: Empty }), 34 | }; 35 | let l = s.len(); 36 | unsafe { 37 | match l { 38 | 1 => Ok(val as u32), 39 | 2 => { 40 | let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 41 | if val2 <= 9 { 42 | Ok((val * 10 + val2) as u32) 43 | } else { 44 | Err(PIE { kind: InvalidDigit }) 45 | } 46 | } 47 | 3 => { 48 | let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 49 | let val3 = s.get_unchecked(2).wrapping_sub(b'0'); 50 | if (val2 <= 9) & (val3 <= 9) { 51 | Ok(val as u32 * 100 + (val2 * 10 + val3) as u32) 52 | } else { 53 | Err(PIE { kind: InvalidDigit }) 54 | } 55 | } 56 | 4 => Ok(parse_4_chars(s)? as u32), 57 | 5 => Ok(val as u32 * 1_0000 + parse_4_chars(&s[1..])? as u32), 58 | 6 => { 59 | let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 60 | if val2 <= 9 { 61 | let result = parse_4_chars(&s[2..])? as u32; 62 | Ok(val as u32 * 10_0000 + val2 as u32 * 1_0000 + result) 63 | } else { 64 | Err(PIE { kind: InvalidDigit }) 65 | } 66 | } 67 | 7 => { 68 | let val2 = parse_4_chars(&s[1..])? as u32 * 100; 69 | let val3 = parse_2_chars(&s[5..])? as u32; 70 | Ok(val as u32 * 1_000_000 + val2 + val3) 71 | } 72 | 8 => parse_8_chars(&s), 73 | 9 => { 74 | let result = parse_8_chars(&s[1..])?; 75 | Ok(result + (val as u32 * 100_000_000)) 76 | } 77 | 10 => { 78 | let mut val2 = s.get_unchecked(1).wrapping_sub(b'0') as u32; 79 | if (val <= 4) & (val2 <= 9) { 80 | let mut result = parse_8_chars(&s[2..])?; 81 | let val = val as u32 * 1_000_000_000; 82 | val2 *= 100_000_000; 83 | result += val; 84 | match result.checked_add(val2) { 85 | Some(val) => Ok(val), 86 | None => Err(PIE { kind: PosOverflow }), 87 | } 88 | } else { 89 | return Err(PIE { kind: PosOverflow }); 90 | } 91 | } 92 | _ => { 93 | let pos = s.iter().position(|byte| *byte != b'0'); 94 | if let Some(pos) = pos { 95 | if l - pos <= 10 { 96 | if s[pos] != b'+' { 97 | return parse_u32(&s[pos..]); 98 | } else { 99 | return Err(PIE { kind: InvalidDigit }); 100 | } 101 | } 102 | } else { 103 | return Ok(0); 104 | } 105 | return Err(PIE { kind: PosOverflow }); 106 | } 107 | } 108 | } 109 | } 110 | 111 | // /// Parses from 0 -> 4_294_967_295 (10 digits and optionally +) 112 | // pub fn parse_u32_old_best(s: &str) -> Result { 113 | // let mut s = s.as_bytes(); 114 | // match s.get(0) { 115 | // Some(val) if *val == b'+' => { 116 | // s = &s[1..]; 117 | // val 118 | // } 119 | // Some(val) => val, 120 | // None => return Err(()), 121 | // }; 122 | // if s.len() < 10 { 123 | // let mut result = 0; 124 | // for c in s { 125 | // let val = c.wrapping_sub(b'0'); 126 | // if val <= 9 { 127 | // result = result * 10 + val as u32; 128 | // } else { 129 | // return Err(()); 130 | // }; 131 | // } 132 | // return Ok(result); 133 | // } else { 134 | // let mut result = 0; 135 | // for c in s { 136 | // let val = c.wrapping_sub(b'0'); 137 | // if val <= 9 { 138 | // result = result * 10 + val as u32; 139 | // } else { 140 | // return Err(()); 141 | // }; 142 | // } 143 | // return Ok(result); 144 | // } 145 | // } 146 | 147 | // pub fn parse_u32_best_15_apr_2021(s: &str) -> Result { 148 | // let mut s = s.as_bytes(); 149 | // let val = match s.get(0) { 150 | // Some(val) => { 151 | // if *val == b'+' { 152 | // s = &s[1..]; 153 | // match s.get(0) { 154 | // Some(val) => val, 155 | // None => return Err(()), 156 | // } 157 | // } else { 158 | // val 159 | // } 160 | // } 161 | // None => return Err(()), 162 | // }; 163 | // let l = s.len(); 164 | // match l { 165 | // 1 => { 166 | // let val = val.wrapping_sub(b'0'); 167 | // if val <= 9 { 168 | // Ok(val as u32) 169 | // } else { 170 | // Err(()) 171 | // } 172 | // } 173 | // 2 => { 174 | // let val = val.wrapping_sub(b'0'); 175 | // let val2 = s[1].wrapping_sub(b'0'); 176 | // if (val > 9) | (val2 > 9) { 177 | // Err(()) 178 | // } else { 179 | // Ok((val * 10 + val2) as u32) 180 | // } 181 | // } 182 | // 3 => { 183 | // let val = val.wrapping_sub(b'0'); 184 | // let val2 = s[1].wrapping_sub(b'0'); 185 | // let val3 = s[2].wrapping_sub(b'0'); 186 | // if (val > 9) | (val2 > 9) | (val3 > 9) { 187 | // Err(()) 188 | // } else { 189 | // Ok(val as u32 * 100 + (val2 * 10 + val3) as u32) 190 | // } 191 | // } 192 | // 4 => Ok(parse_4_chars(s)? as u32), 193 | // 5 => { 194 | // let mut result = parse_4_chars(s)? as u32; 195 | // result *= 10; 196 | // let val = s[4].wrapping_sub(b'0'); 197 | // if val > 9 { 198 | // Err(()) 199 | // } else { 200 | // Ok(result + val as u32) 201 | // } 202 | // } 203 | // 6 => { 204 | // let mut result = parse_4_chars(s)? as u32; 205 | // result *= 100; 206 | // let val = parse_2_chars(&s[4..])?; 207 | // result += val as u32; 208 | // Ok(result) 209 | // } 210 | // 7 => { 211 | // let mut result = parse_4_chars(s)? as u32; 212 | // result *= 100; 213 | // let val = parse_2_chars(&s[4..])?; 214 | // result += val as u32; 215 | // result *= 10; 216 | // let val = s[6].wrapping_sub(b'0'); 217 | // if val > 9 { 218 | // return Err(()); 219 | // } 220 | // Ok(result + val as u32) 221 | // } 222 | // 8 => parse_8_chars(&s), 223 | // 9 => { 224 | // let val = val.wrapping_sub(b'0') as u32; 225 | // let result = parse_8_chars(&s[1..])?; 226 | // if val > 9 { 227 | // return Err(()); 228 | // } 229 | // Ok(result + (val as u32 * 100_000_000)) 230 | // } 231 | // 10 => { 232 | // let mut val = val.wrapping_sub(b'0') as u32; 233 | // let mut val2 = s[1].wrapping_sub(b'0') as u32; 234 | // if (val > 4) | (val2 > 9) { 235 | // return Err(()); 236 | // } 237 | // let mut result = parse_8_chars(&s[2..])?; 238 | // val *= 1_000_000_000; 239 | // val2 *= 100_000_000; 240 | // result += val; 241 | // match result.checked_add(val2) { 242 | // Some(val) => Ok(val), 243 | // None => Err(()), 244 | // } 245 | // } 246 | // _ => Err(()), 247 | // } 248 | // } 249 | 250 | // pub fn parse_u32_challengerXX(s: &str) -> Result { 251 | // let mut s = s.as_bytes(); 252 | // let mut l = s.len(); 253 | // let val = match s.get(0) { 254 | // Some(val) => { 255 | // if *val != b'+' { 256 | // if l == 1 { 257 | // let val = val.wrapping_sub(b'0'); 258 | // return if val <= 9 { 259 | // Ok(val as u32) 260 | // } else { 261 | // Err(PIE { 262 | // kind: IntErrorKind::InvalidDigit, 263 | // }) 264 | // } 265 | // } 266 | // val 267 | // } else { 268 | // s = &s[1..]; 269 | // match s.get(0) { 270 | // Some(val) => { 271 | // l -= 1; 272 | // if l == 1 { 273 | // let val = val.wrapping_sub(b'0'); 274 | // return if val <= 9 { 275 | // Ok(val as u32) 276 | // } else { 277 | // Err(PIE { 278 | // kind: IntErrorKind::InvalidDigit, 279 | // }) 280 | // } 281 | // } 282 | // val 283 | // }, 284 | // None => return Err(PIE { kind: InvalidDigit }), 285 | // } 286 | // } 287 | // } 288 | // None => return Err(PIE { kind: Empty }), 289 | // }; 290 | // match l { 291 | // // 1 => { 292 | // // let val = val.wrapping_sub(b'0'); 293 | // // if val <= 9 { 294 | // // Ok(val as u32) 295 | // // } else { 296 | // // Err(PIE { 297 | // // kind: IntErrorKind::InvalidDigit, 298 | // // }) 299 | // // } 300 | // // } 301 | // 2 => { 302 | // let val = val.wrapping_sub(b'0'); 303 | // let val2 = s[1].wrapping_sub(b'0'); 304 | // if (val <= 9) & (val2 <= 9) { 305 | // Ok((val * 10 + val2) as u32) 306 | // } else { 307 | // Err(PIE { 308 | // kind: IntErrorKind::InvalidDigit, 309 | // }) 310 | // } 311 | // } 312 | // 3 => { 313 | // let val = val.wrapping_sub(b'0'); 314 | // let val2 = s[1].wrapping_sub(b'0'); 315 | // let val3 = s[2].wrapping_sub(b'0'); 316 | // if (val <= 9) & (val2 <= 9) & (val3 <= 9) { 317 | // Ok(val as u32 * 100 + (val2 * 10 + val3) as u32) 318 | // } else { 319 | // Err(PIE { 320 | // kind: IntErrorKind::InvalidDigit, 321 | // }) 322 | // } 323 | // } 324 | // 4 => Ok(parse_4_chars(s)? as u32), 325 | // 5 => { 326 | // let val = val.wrapping_sub(b'0'); 327 | // if val <= 9 { 328 | // Ok(val as u32 * 1_0000 + parse_4_chars(&s[1..])? as u32) 329 | // } else { 330 | // Err(PIE { 331 | // kind: IntErrorKind::InvalidDigit, 332 | // }) 333 | // } 334 | // } 335 | // 6 => { 336 | // // let result = parse_4_chars(s)? as u32 * 100; 337 | // // let val = parse_2_chars(&s[4..])?; 338 | // parse_6_chars(s) 339 | // } 340 | // 7 => { 341 | // let result = parse_6_chars(&s[1..])?;//parse_4_chars(s)? as u32 * 100; 342 | // // let val = parse_2_chars(&s[4..])?; 343 | // let val = val.wrapping_sub(b'0'); 344 | // // result += val as u32; 345 | // // result *= 10; 346 | // // let val = s[6].wrapping_sub(b'0'); 347 | // if val <= 9 { 348 | // Ok((val as u32) * 1000_000 + result) 349 | // } else { 350 | // Err(PIE { 351 | // kind: IntErrorKind::InvalidDigit, 352 | // }) 353 | // } 354 | // } 355 | // 8 => parse_8_chars(&s), 356 | // 9 => { 357 | // let val = val.wrapping_sub(b'0') as u32; 358 | // let result = parse_8_chars(&s[1..])?; 359 | // if val <= 9 { 360 | // Ok(result + (val * 100_000_000)) 361 | // } else { 362 | // Err(PIE { 363 | // kind: IntErrorKind::InvalidDigit, 364 | // }) 365 | // } 366 | // } 367 | // 10 => { 368 | // let mut val = val.wrapping_sub(b'0') as u32; 369 | // let mut val2 = s[1].wrapping_sub(b'0') as u32; 370 | // if (val <= 4) & (val2 <= 9) { 371 | // let mut result = parse_8_chars(&s[2..])?; 372 | // val *= 1_000_000_000; 373 | // val2 *= 100_000_000; 374 | // result += val; 375 | // match result.checked_add(val2) { 376 | // Some(val) => Ok(val), 377 | // None => Err(PIE { kind: PosOverflow }), 378 | // } 379 | // } else { 380 | // return Err(PIE { kind: PosOverflow }); 381 | // } 382 | // } 383 | // _ => Err(PIE { kind: PosOverflow }), 384 | // } 385 | // } 386 | 387 | // /// Parses from 0 -> 4_294_967_295 (10 digits and optionally +) 388 | // pub fn parse_u32_challenger(s: &[u8]) -> Result { 389 | // //TODO: can we accept AsRef<[u8]> or IntoRef ? 390 | // let mut s = s; //.as_bytes(); 391 | // let val = match s.get(0) { 392 | // Some(val) => { 393 | // let val = val.wrapping_sub(b'0'); 394 | // if val <= 9 { 395 | // val 396 | // } else { 397 | // if val == PLUS { 398 | // s = &s[1..]; 399 | // match s.get(0) { 400 | // Some(val2) => { 401 | // let val2 = (*val2).wrapping_sub(b'0'); 402 | // if val2 <= 9 { 403 | // val2 404 | // } else { 405 | // return Err(PIE { kind: InvalidDigit }); 406 | // } 407 | // } 408 | // None => return Err(PIE { kind: InvalidDigit }), 409 | // } 410 | // } else { 411 | // return Err(PIE { kind: InvalidDigit }); 412 | // } 413 | // } 414 | // } 415 | // None => return Err(PIE { kind: Empty }), 416 | // }; 417 | // let l = s.len(); 418 | // unsafe { 419 | // match l { 420 | // 1 => Ok(val as u32), 421 | // 2 => { 422 | // let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 423 | // if val2 <= 9 { 424 | // Ok((val * 10 + val2) as u32) 425 | // } else { 426 | // Err(PIE { kind: InvalidDigit }) 427 | // } 428 | // } 429 | // 3 => { 430 | // let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 431 | // let val3 = s.get_unchecked(2).wrapping_sub(b'0'); 432 | // if (val2 <= 9) & (val3 <= 9) { 433 | // Ok(val as u32 * 100 + (val2 * 10 + val3) as u32) 434 | // } else { 435 | // Err(PIE { kind: InvalidDigit }) 436 | // } 437 | // } 438 | // 4 => Ok(parse_4_chars(s)? as u32), 439 | // 5 => Ok(val as u32 * 1_0000 + parse_4_chars(&s[1..])? as u32), 440 | // 6 => { 441 | // let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 442 | // if val2 <= 9 { 443 | // let result = parse_4_chars(&s[2..])? as u32; 444 | // Ok(val as u32 * 10_0000 + val2 as u32 * 1_0000 + result) 445 | // } else { 446 | // Err(PIE { kind: InvalidDigit }) 447 | // } 448 | // } 449 | // 7 => { 450 | // let val2 = parse_4_chars(&s[1..])? as u32 * 100; 451 | // //let val3 = parse_2_chars(&s[5..])? as u32; 452 | // let val3 = s.get_unchecked(5).wrapping_sub(b'0'); 453 | // let val4 = s.get_unchecked(6).wrapping_sub(b'0'); 454 | // if (val3 <= 9) & (val4 <= 9) { 455 | // Ok(val as u32 * 1_000_000 + val2 + (val3 * 10) as u32 + val4 as u32) 456 | // } else { 457 | // Err(PIE { kind: InvalidDigit }) 458 | // } 459 | // } 460 | // 8 => parse_8_chars(&s), 461 | // 9 => { 462 | // parse_8_chars(&s[1..]).map(|val2| (val as u32 * 100_000_000) + val2) 463 | // // let result = parse_8_chars(&s[1..])?; 464 | // // Ok(result + (val as u32 * 100_000_000)) 465 | // } 466 | // 10 => { 467 | // let mut val2 = s.get_unchecked(1).wrapping_sub(b'0') as u32; 468 | // if (val <= 4) & (val2 <= 9) { 469 | // let mut result = parse_8_chars(&s[2..])?; 470 | // let val = val as u32 * 1_000_000_000; 471 | // val2 *= 100_000_000; 472 | // result += val; 473 | // match result.checked_add(val2) { 474 | // Some(val) => Ok(val), 475 | // None => Err(PIE { kind: PosOverflow }), 476 | // } 477 | // } else { 478 | // return Err(PIE { kind: PosOverflow }); 479 | // } 480 | // } 481 | // _ => Err(PIE { kind: PosOverflow }), 482 | // } 483 | // } 484 | // } 485 | 486 | pub fn parse_u32_challenger(ss: &[u8]) -> Result { 487 | super::parse::(ss).map_err(|_| PIE { kind: InvalidDigit }) 488 | } 489 | 490 | // DEAD END: Tried to fold in the check of '+' but not fast enough. 491 | // pub fn parse_u32(s: &str) -> Result { 492 | // let s = s.as_bytes(); 493 | // let l = s.len(); 494 | // unsafe { 495 | // match l { 496 | // 1 => { 497 | // let val = s.get_unchecked(0).wrapping_sub(b'0'); 498 | // return if val < 10 { 499 | // Ok(val as u32) 500 | // } else { 501 | // Err(()) 502 | // }; 503 | // } 504 | // 2 => { 505 | // let val = s.get_unchecked(0).wrapping_sub(b'0'); 506 | // let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 507 | // if (val < 10) & (val2 < 10) { 508 | // return Ok((val * 10 + val2) as u32); 509 | // } 510 | // if (val == PLUS) & (val2 < 10) { 511 | // return Ok(val2 as u32) 512 | // } 513 | // return Err(()); 514 | // } 515 | // 3 => { 516 | // let val = s.get_unchecked(0).wrapping_sub(b'0'); 517 | // let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 518 | // let val3 = s.get_unchecked(2).wrapping_sub(b'0'); 519 | // if (val < 10 ) & (val2 < 10) & (val3 < 10) { 520 | // return Ok(val as u32 * 100 + (val2 * 10 + val3) as u32) 521 | // }; 522 | // if (val == PLUS) & (val2 < 10) & (val3 < 10) { 523 | // return Ok((val2 * 10 + val3) as u32); 524 | // } 525 | // return Err(()); 526 | // } 527 | // 4 => match parse_4_chars(s) { 528 | // Ok(val) => return Ok(val as u32), 529 | // Err(_) => { 530 | // let val = s.get_unchecked(0).wrapping_sub(b'0'); 531 | // let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 532 | // let val3 = s.get_unchecked(2).wrapping_sub(b'0'); 533 | // if (val < 10 ) & (val2 < 10) & (val3 < 10) { 534 | // return Ok(val as u32 * 100 + (val2 * 10 + val3) as u32) 535 | // }; 536 | // return Err(()); 537 | // } 538 | // } 539 | // 5 => { 540 | // let val = s.get_unchecked(0).wrapping_sub(b'0'); 541 | // if val <= 9 { 542 | // Ok(val as u32 * 10_000 + parse_4_chars(&s.get_unchecked(1..))? as u32) 543 | // } else { 544 | // if val == PLUS { 545 | // return parse_4_chars(&s.get_unchecked(1..)).map(|val| val as u32); 546 | // } 547 | // Err(()) 548 | // } 549 | // } 550 | // 6 => { 551 | // match parse_2_chars(&s) { 552 | // Ok(val) => { 553 | // let result = parse_4_chars(s.get_unchecked(2..))? as u32; 554 | // Ok(result + val as u32 * 10_000) 555 | // }, 556 | // Err(_) => { 557 | // let val = s.get_unchecked(0); 558 | // if *val == b'+' { 559 | // let val2 = s.get_unchecked(1).wrapping_sub(b'0'); 560 | // if val2 < 10 { 561 | // return Ok(val2 as u32 * 10_000 + parse_4_chars(s.get_unchecked(2..))? as u32) 562 | // } 563 | // } 564 | // Err(()) 565 | // } 566 | // } 567 | // } 568 | // 7 => { 569 | // let val = s.get_unchecked(0).wrapping_sub(b'0'); 570 | // if val <= 9 { 571 | // let result = parse_4_chars(&s.get_unchecked(1..))? as u32 * 100; 572 | // let loose_change = parse_2_chars(&s.get_unchecked(5..))? as u32; 573 | // Ok(result + loose_change + (val as u32 * 1000_000)) 574 | // } else { 575 | // if val == PLUS { 576 | // let result = parse_4_chars(&s[1..])? as u32 * 100; 577 | // let loose_change = parse_2_chars(&s.get_unchecked(5..))? as u32; 578 | // return Ok(result + loose_change) 579 | // } 580 | // Err(()) 581 | // } 582 | // } 583 | // 8 => { 584 | // match parse_8_chars(&s) { 585 | // Ok(val) => Ok(val), 586 | // Err(_) => { 587 | // if *s.get_unchecked(0) == b'+' { 588 | // let val = s.get_unchecked(1).wrapping_sub(b'0'); 589 | // if val <= 9 { 590 | // let result = parse_4_chars(&s.get_unchecked(2..))? as u32 * 100; 591 | // let loose_change = parse_2_chars(&s.get_unchecked(6..))? as u32; 592 | // return Ok((val as u32 * 100_0000) + result + loose_change) 593 | // } 594 | // } 595 | // Err(()) 596 | // } 597 | // } 598 | // }, 599 | // 9 => { 600 | // let val = s.get_unchecked(0).wrapping_sub(b'0'); 601 | // if val <= 9 { 602 | // let result = parse_8_chars(&s.get_unchecked(1..))?; 603 | // Ok(val as u32 * 1_0000_0000 + result) 604 | // } else { 605 | // if val == PLUS { 606 | // return parse_8_chars(&s.get_unchecked(1..)); 607 | // } 608 | // Err(()) 609 | // } 610 | // } 611 | // 10 => { 612 | // let val = s.get_unchecked(0).wrapping_sub(b'0'); 613 | // let mut val2 = s.get_unchecked(1).wrapping_sub(b'0') as u32; 614 | // if (val <= 4) & (val2 <= 9) { 615 | // let mut result = parse_8_chars(&s.get_unchecked(2..))?; 616 | // let val = val as u32 * 1_000_000_000; 617 | // val2 *= 100_000_000; 618 | // result += val; 619 | // match result.checked_add(val2) { 620 | // Some(val) => Ok(val), 621 | // None => Err(()), 622 | // } 623 | // } else { 624 | // if val == PLUS { 625 | // if val2 > 9 { 626 | // return Err(()); 627 | // }; 628 | // let result = parse_8_chars(&s.get_unchecked(2..))?; 629 | // return Ok(val2 as u32 * 1_0000_0000 + result) 630 | // } 631 | // Err(()) 632 | // } 633 | // } 634 | // 11 => { 635 | // if *s.get_unchecked(0) != b'+' { 636 | // return Err(()) 637 | // } 638 | // let s = s.get_unchecked(1..); 639 | // let val = s.get_unchecked(0).wrapping_sub(b'0'); 640 | // let mut val2 = s.get_unchecked(1).wrapping_sub(b'0') as u32; 641 | // if (val > 4) | (val2 > 9) { 642 | // return Err(()); 643 | // } 644 | // let mut result = parse_8_chars(&s.get_unchecked(2..))?; 645 | // let val = val as u32 * 1_000_000_000; 646 | // val2 *= 100_000_000; 647 | // result += val; 648 | // match result.checked_add(val2) { 649 | // Some(val) => Ok(val), 650 | // None => Err(()), 651 | // } 652 | // } 653 | // _ => Err(()), 654 | // } 655 | // } 656 | // } 657 | 658 | // pub fn parse_u32(s: &str) -> Result { 659 | // unsafe { 660 | // let mut s = s.as_bytes(); 661 | // let (val, val2) = match s.get(0) { 662 | // Some(val) => { 663 | // let val = if *val == b'+' { 664 | // s = &s.get_unchecked(1..); 665 | // match s.get(0) { 666 | // Some(val) => val, 667 | // None => return Err(()), 668 | // } 669 | // } else { 670 | // val 671 | // }; 672 | 673 | // let val2 = match s.get(1) { 674 | // Some(val2) => val2, 675 | // None => { 676 | // let val = val.wrapping_sub(b'0'); 677 | // return if val <= 9 { Ok(val as u32) } else { Err(()) }; 678 | // } 679 | // }; 680 | 681 | // (val, val2) 682 | // } 683 | // None => return Err(()), 684 | // }; 685 | 686 | // let l = s.len(); 687 | // let mut res = 0; 688 | // if l >= 10 { 689 | // if l > 10 { 690 | // return Err(()); 691 | // } 692 | // let val = val.wrapping_sub(b'0'); 693 | // let val2 = val2.wrapping_sub(b'0'); 694 | // if (val > 4) | (val2 > 9) { 695 | // return Err(()); 696 | // }; 697 | // let val = val as u32 * TENS_U32.get_unchecked(9); 698 | // let val2 = val2 as u32 * TENS_U32.get_unchecked(8); 699 | // s = &s.get_unchecked(2..); 700 | 701 | // match val.checked_add(val2 + parse_8_chars(&s)?) { 702 | // Some(val) => return Ok(val), 703 | // None => return Err(()) 704 | // }; 705 | // } 706 | // if l & 2 != 0 { 707 | // let val = val.wrapping_sub(b'0'); 708 | // let val2 = val2.wrapping_sub(b'0'); 709 | // if (val > 9) | (val2 > 9) { 710 | // return Err(()); 711 | // }; 712 | // res += val as u32 * TENS_U32.get_unchecked(s.len() - 1) 713 | // + val2 as u32 * TENS_U32.get_unchecked(s.len() - 2); 714 | // s = &s.get_unchecked(2..); 715 | // } 716 | // if l & 8 != 0 { 717 | // let val = parse_8_chars(&s)?; 718 | // s = &s.get_unchecked(8..); 719 | // res += val * TENS_U32.get_unchecked(s.len()); 720 | // } 721 | // if l & 4 != 0 { 722 | // let val = parse_4_chars(&s)? as u32; 723 | // s = &s.get_unchecked(4..); 724 | // res += val * TENS_U32.get_unchecked(s.len()); 725 | // } 726 | // if l & 1 != 0 { 727 | // let val = s.get_unchecked(0).wrapping_sub(b'0'); 728 | // if val > 9 { 729 | // return Err(()); 730 | // }; 731 | // res += val as u32; 732 | // } 733 | // Ok(res) 734 | // } 735 | // } 736 | 737 | // pub fn parse_u32_old(s: &str) -> Result { 738 | // let mut s = s.as_bytes(); 739 | // let (val, val2) = match s.get(0) { 740 | // Some(val) => { 741 | // let val = if *val == b'+' { 742 | // s = &s[1..]; 743 | // match s.get(0) { 744 | // Some(val) => val, 745 | // None => return Err(()), 746 | // } 747 | // } else { 748 | // val 749 | // }; 750 | 751 | // let val2 = match s.get(1) { 752 | // Some(val2) => val2, 753 | // None => { 754 | // let val = val.wrapping_sub(b'0'); 755 | // return if val <= 9 { Ok(val as u32) } else { Err(()) }; 756 | // } 757 | // }; 758 | 759 | // (val, val2) 760 | // } 761 | // None => return Err(()), 762 | // }; 763 | // let l = s.len(); 764 | // match l { 765 | // 2 => { 766 | // let val = val.wrapping_sub(b'0'); 767 | // let val2 = val2.wrapping_sub(b'0'); 768 | // if (val > 9) | (val2 > 9) { 769 | // return Err(()); 770 | // }; 771 | // Ok((val * 10 + val2) as u32) 772 | // } 773 | // 3 => { 774 | // let val = val.wrapping_sub(b'0'); 775 | // let val2 = val2.wrapping_sub(b'0'); 776 | // let val3 = s[2].wrapping_sub(b'0'); 777 | // if (val > 9) | (val2 > 9) | (val3 > 9) { 778 | // return Err(()); 779 | // }; 780 | // Ok(val as u32 * 100 + (val2 * 10 + val3) as u32) 781 | // } 782 | // 4 => Ok(parse_4_chars(s)? as u32), 783 | // 5 => { 784 | // let result = parse_4_chars(&s[1..])? as u32; 785 | // let val = val.wrapping_sub(b'0'); 786 | // if val > 9 { 787 | // return Err(()); 788 | // } 789 | // Ok(result + (val as u32 * 10_000)) 790 | // } 791 | // 6 => { 792 | // let mut result = parse_4_chars(s)? as u32; 793 | // result *= 100; 794 | // let val = parse_2_chars(&s[4..])?; 795 | // result += val as u32; 796 | // Ok(result) 797 | // } 798 | // 7 => { 799 | // let result = parse_4_chars(&s[1..])? as u32; 800 | // let loose_change = parse_2_chars(&s[5..])? as u32; 801 | // let val = val.wrapping_sub(b'0') as u32; 802 | // if val > 9 { 803 | // return Err(()); 804 | // } 805 | // Ok(val * 1_000_000 + result * 100 + loose_change) 806 | // } 807 | // 8 => parse_8_chars(&s), 808 | // 9 => { 809 | // let val = val.wrapping_sub(b'0') as u32; 810 | // let result = parse_8_chars(&s[1..])?; 811 | // if val > 9 { 812 | // return Err(()); 813 | // } 814 | // Ok(result + (val as u32 * 100_000_000)) 815 | // } 816 | // 10 => { 817 | // let mut val = val.wrapping_sub(b'0') as u32; 818 | // let mut val2 = val2.wrapping_sub(b'0') as u32; 819 | // if (val > 4) | (val2 > 9) { 820 | // return Err(()); 821 | // } 822 | // let mut result = parse_8_chars(&s[2..])?; 823 | // val *= 1_000_000_000; 824 | // val2 *= 100_000_000; 825 | // result += val; 826 | // match result.checked_add(val2) { 827 | // Some(val) => Ok(val), 828 | // None => Err(()), 829 | // } 830 | // } 831 | // _ => Err(()), 832 | // } 833 | // } 834 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | //#![cfg_attr(all(feature = "nightly", feature = "simd"), feature(stdsimd))] 3 | #![allow(clippy::inconsistent_digit_grouping)] 4 | #![warn(unsafe_op_in_unsafe_fn)] 5 | #[macro_use] 6 | mod parse; 7 | 8 | //use log::*; 9 | // mod parse_i16; 10 | // mod parse_i32; 11 | // mod parse_i8; 12 | // mod parse_u16; 13 | // mod parse_u32; 14 | // mod parse_u8; 15 | mod trees; 16 | 17 | use core::num::IntErrorKind; 18 | 19 | pub use parse::{parse, parse_challenger, parse_from_str, FromStrRadixHelper}; 20 | // pub use parse_i16::{parse_i16, parse_i16_challenger}; 21 | // pub use parse_i32::{parse_i32, parse_i32_challenger}; 22 | // pub use parse_i8::{parse_i8, parse_i8_challenger}; 23 | // pub use parse_u16::{parse_u16, parse_u16_challenger}; 24 | // pub use parse_u32::{parse_u32, parse_u32_challenger}; 25 | // pub use parse_u8::{parse_u8, parse_u8_challenger}; 26 | 27 | const PLUS: u8 = b'+'.wrapping_sub(b'0'); 28 | const MINUS: u8 = b'-'.wrapping_sub(b'0'); 29 | 30 | /// A public version of `std::num::ParseIntError` 31 | #[derive(Debug, Eq, PartialEq)] 32 | pub struct ParseIntErrorPublic { 33 | pub kind: IntErrorKind, 34 | } 35 | 36 | type Pie = ParseIntErrorPublic; 37 | 38 | /// Parse the first 32 chars in a u8 slice as a base 10 integer. 39 | /// SAFETY: Do not call with a string length less than that. 40 | /// SAFETY: slice must be 16 byte aligned. 41 | #[doc(hidden)] 42 | #[cfg(not(all(target_feature = "avx", feature = "simd")))] 43 | #[cfg(target_endian = "little")] 44 | #[inline] 45 | pub unsafe fn parse_32_chars(mut s: &[u8]) -> Result { 46 | debug_assert!(s.len() >= 32); 47 | let val16 = unsafe { parse_16_chars(s)? as u128 }; 48 | s = &s[16..]; 49 | let res = val16 * 1_0000_0000_0000_0000; 50 | 51 | // Do the same thing again as a parse_32_chars fn would need 256bits. 52 | let val16 = unsafe { parse_16_chars(s)? as u128 }; 53 | Ok(res + val16) 54 | } 55 | 56 | /// For now not going to do simd stuff for big-endien... 57 | /// SAFETY: Do not call with a string length less than that. 58 | /// SAFETY: slice must be 16 byte aligned. 59 | #[doc(hidden)] 60 | #[cfg(not(target_endian = "little"))] 61 | #[inline] 62 | pub unsafe fn parse_32_chars(mut s: &[u8]) -> Result { 63 | debug_assert!(s.len() >= 32); 64 | let val16 = unsafe { parse_16_chars(s)? as u128 }; 65 | s = &s[16..]; 66 | let res = val16 * 1_0000_0000_0000_0000; 67 | 68 | // Do the same thing again as a parse_32_chars fn would need 256bits. 69 | let val16 = unsafe { parse_16_chars(s)? as u128 }; 70 | Ok(res + val16) 71 | } 72 | 73 | // /// Parse the first 32 chars in a u8 slice as a base 10 integer. 74 | // /// SAFETY: Do not call with a string length less than that. 75 | // #[doc(hidden)] 76 | // #[cfg(all(target_feature = "avx", feature = "simd"))] 77 | // #[inline] 78 | // pub unsafe fn parse_32_chars(s: &[u8]) -> Result { 79 | // debug_assert!(s.len() >= 32); 80 | 81 | // use core::arch::x86_64::{ 82 | // _mm256_hadd_epi32, _mm256_lddqu_si256, _mm256_madd_epi16, _mm256_maddubs_epi16, 83 | // }; 84 | // use core::mem::transmute; 85 | // use wide::*; 86 | // let MULT10: i8x32 = i8x32::from([ 87 | // 10_i8, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10_i8, 1, 10, 1, 10, 1, 10, 1, 88 | // 10, 1, 10, 1, 10, 1, 10, 1, 89 | // ]); 90 | 91 | // let MULT100: i16x16 = i16x16::from([ 92 | // 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 93 | // ]); 94 | // let MULT10000: i32x8 = i32x8::from([10000, 1, 10000, 1, 10000, 1, 10000, 1]); 95 | // let ZEROS: i8x32 = i8x32::splat(b'0' as i8); 96 | // let ZERO_TO_LOWEST: i8x32 = i8x32::splat(-128); 97 | // let UPPER_BOUND: i8x32 = i8x32::splat(-128 + 10); 98 | 99 | // let chunk: i8x32 = unsafe { transmute(_mm256_lddqu_si256(core::mem::transmute_copy(&s))) }; 100 | // let chunk = chunk - ZEROS; //will wrap 101 | // chunk stores individual digits 102 | // let chunk_og = chunk; 103 | // let digits_at_lowest = chunk_og + ZERO_TO_LOWEST; 104 | // let range_chk1 = i8x32::cmp_lt(digits_at_lowest, UPPER_BOUND); 105 | // let x: [u128; 2] = transmute(range_chk1); 106 | // let is_valid = x[0] == 0 && x[1] == 0; //TODO invert? 107 | 108 | // chunk stores individual digits 109 | // let chunk: i16x16 = 110 | // unsafe { transmute(_mm256_maddubs_epi16(transmute(chunk), transmute(MULT10))) }; 111 | // chunk stores pairs of digits 112 | // let chunk: i32x8 = 113 | // unsafe { transmute(_mm256_madd_epi16(transmute(chunk), transmute(MULT100))) }; 114 | // chunk stores quads of digits 115 | // let res = chunk * MULT10000; 116 | // let chunk: i32x8 = unsafe { transmute(_mm256_hadd_epi32(transmute(res), transmute(res))) }; 117 | // chunk stores octs of digits 118 | // let chunk: [i32; 8] = unsafe { transmute(chunk) }; 119 | 120 | // if likely!(is_valid) { 121 | // let upper = chunk[2] as u64 * 10000_0000_u64 + chunk[3] as u64; 122 | // let lower = chunk[4] as u64 * 10000_0000_u64 + chunk[5] as u64; 123 | 124 | // let result = upper as u128 * 1_0000_0000_0000_0000_u128 + lower as u128; 125 | 126 | // Ok(result as u128) 127 | // } else { 128 | // Err(Pie { 129 | // kind: IntErrorKind::InvalidDigit, 130 | // }) 131 | // } 132 | // } 133 | 134 | /// Parse the first 32 chars in a u8 slice as a base 10 integer. 135 | /// SAFETY: Do not call with a string length less than that. 136 | #[doc(hidden)] 137 | #[cfg(all(target_feature = "avx", feature = "simd"))] 138 | #[inline] 139 | pub fn parse_32_chars(s: &[u8]) -> Result { 140 | debug_assert!(s.len() >= 32); 141 | 142 | use core::arch::x86_64::{ 143 | _mm256_add_epi8, _mm256_cmpgt_epi8, _mm256_hadd_epi32, _mm256_lddqu_si256, 144 | _mm256_madd_epi16, _mm256_maddubs_epi16, _mm256_mullo_epi32, _mm256_set1_epi8, 145 | _mm256_set_epi16, _mm256_set_epi32, _mm256_set_epi8, _mm256_sub_epi16, _mm256_testz_si256, 146 | }; 147 | 148 | unsafe { 149 | let chunk = _mm256_lddqu_si256(core::mem::transmute_copy(&s)); 150 | let zeros = _mm256_set1_epi8(b'0' as i8); 151 | let chunk = _mm256_sub_epi16(chunk, zeros); //will wrap 152 | // chunk stores individual digits 153 | 154 | let zero_to_lowest = _mm256_set1_epi8(-128); 155 | let digits_at_lowest = _mm256_add_epi8(chunk, zero_to_lowest); 156 | 157 | let upper_bound = _mm256_set1_epi8(-128 + 9); 158 | 159 | let is_valid = _mm256_cmpgt_epi8(digits_at_lowest, upper_bound); 160 | let is_valid = _mm256_testz_si256(is_valid, is_valid) != 0; 161 | 162 | let mult10 = _mm256_set_epi8( 163 | 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 164 | 10, 1, 10, 1, 10, 1, 10, 165 | ); 166 | let mult100 = _mm256_set_epi16( 167 | 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 168 | ); 169 | let mult10000 = _mm256_set_epi32(1, 10000, 1, 10000, 1, 10000, 1, 10000); 170 | 171 | // chunk stores individual digits 172 | let chunk = _mm256_maddubs_epi16(chunk, mult10); 173 | 174 | // chunk stores pairs of digits 175 | //error!("chunk pair: {:?}",fmt_chunk(chunk)); 176 | let chunk = _mm256_madd_epi16(chunk, mult100); 177 | 178 | // chunk stores quads of digits 179 | // The fuzed mul-add instruction doesn't exist so we need to do it in two steps 180 | let chunk = _mm256_mullo_epi32(chunk, mult10000); 181 | let chunk = _mm256_hadd_epi32(chunk, chunk); 182 | //error!("chunk hadd: {:?}", fmt_chunk32(chunk)); 183 | 184 | // chunk stores octs of digits 185 | let chunk: [i32; 8] = core::mem::transmute(chunk); 186 | if is_valid { 187 | // If you're going to move these out of the is_valid block 188 | // then watch for overflows. 189 | let upper = chunk[2] as u64 * 10000_0000_u64 + chunk[3] as u64; 190 | let lower = chunk[4] as u64 * 10000_0000_u64 + chunk[5] as u64; 191 | 192 | let result = upper as u128 * 1_0000_0000_0000_0000_u128 + lower as u128; 193 | Ok(result) 194 | } else { 195 | Err(Pie { 196 | kind: IntErrorKind::InvalidDigit, 197 | }) 198 | } 199 | } 200 | } 201 | 202 | // fn fmt_chunk(chunk: core::arch::x86_64::__m256i) -> [u8; 32] { 203 | // unsafe { 204 | // core::mem::transmute(chunk) 205 | // } 206 | // } 207 | 208 | // fn fmt_chunk16(chunk: core::arch::x86_64::__m256i) -> [u16; 16] { 209 | // unsafe { 210 | // core::mem::transmute(chunk) 211 | // } 212 | // } 213 | // fn fmt_chunk32(chunk: core::arch::x86_64::__m256i) -> [u32; 8] { 214 | // unsafe { 215 | // core::mem::transmute(chunk) 216 | // } 217 | // } 218 | 219 | /// Parse the first 16 chars in a u8 slice as a base 10 integer. 220 | /// SAFETY: Do not call with a string length less than that. 221 | // #[cfg(all(target_feature = "sse2", feature = "simd"))] 222 | // #[inline] 223 | // #[doc(hidden)] 224 | // pub unsafe fn parse_16_chars(s: &[u8]) -> Result { 225 | // debug_assert!(s.len() >= 16); 226 | 227 | // use core::arch::x86_64::{ 228 | // _mm_lddqu_si128, _mm_madd_epi16, _mm_maddubs_epi16, _mm_packus_epi32, 229 | // }; 230 | // use core::mem::transmute; 231 | // use safe_arch::*; 232 | // //TODO: waiting on https://github.com/rust-lang/stdsimd/issues/102 233 | // let chunk: i8x16 = unsafe { transmute(_mm_lddqu_si128(core::mem::transmute_copy(&s))) }; 234 | // let ZEROS: i8x16 = i8x16::splat(b'0' as i8); 235 | 236 | // let chunk = chunk - ZEROS; //will wrap 237 | 238 | // let ZERO_TO_LOWEST: i8x16 = i8x16::splat(-128); 239 | // let digits_at_lowest = chunk + ZERO_TO_LOWEST; 240 | 241 | // let UPPER_BOUND: i8x16 = i8x16::splat(-128 + 10); 242 | // let range_chk1 = i8x16::cmp_lt(digits_at_lowest, UPPER_BOUND); 243 | // let is_valid = range_chk1.all(); 244 | 245 | // let MULT10: i8x16 = 246 | // unsafe { i8x16::from([10_i8, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1]) }; 247 | 248 | // let chunk: i8x16 = unsafe { transmute(_mm_maddubs_epi16(transmute(chunk), transmute(MULT10))) }; 249 | 250 | // let MULT100: i16x8 = unsafe { i16x8::from([100, 1, 100, 1, 100, 1, 100, 1]) }; 251 | 252 | // let chunk: i16x8 = unsafe { transmute(_mm_madd_epi16(transmute(chunk), transmute(MULT100))) }; 253 | 254 | // let chunk = unsafe { _mm_packus_epi32(transmute(chunk), transmute(chunk)) }; 255 | // let MULT10000: i16x8 = unsafe { i16x8::from([10000, 1, 10000, 1, 10000, 1, 10000, 1]) }; 256 | 257 | // let chunk: i64x2 = unsafe { transmute(_mm_madd_epi16(chunk, transmute(MULT10000))) }; 258 | // let chunk: [u64; 2] = transmute(chunk); //From::<[i64;2]>(chunk)[1].unsigned_abs(); //this could just be a transmute 259 | // let chunk = chunk[1]; 260 | // let chunk = ((chunk & 0xffffffff) * 1_0000_0000) + (chunk >> 32); 261 | // if likely!(is_valid) { 262 | // Ok(chunk) 263 | // } else { 264 | // Err(Pie { 265 | // kind: IntErrorKind::InvalidDigit, 266 | // }) 267 | // } 268 | // } 269 | 270 | /// Parse the first 16 chars in a u8 slice as a base 10 integer. 271 | /// SAFETY: Do not call with a string length less than that. 272 | /// SAFETY: Aligned / unaligned is fine. 273 | #[cfg(all(target_feature = "sse2", feature = "simd"))] 274 | #[inline] 275 | #[doc(hidden)] 276 | pub unsafe fn parse_16_chars(s: &[u8]) -> Result { 277 | debug_assert!(s.len() >= 16); 278 | 279 | use core::arch::x86_64::{ 280 | _mm_add_epi8, _mm_cmplt_epi8, _mm_cvtsi128_si64, _mm_lddqu_si128, _mm_madd_epi16, 281 | _mm_maddubs_epi16, _mm_packus_epi32, _mm_set1_epi8, _mm_set_epi16, _mm_set_epi8, 282 | _mm_sub_epi16, _mm_test_all_ones, 283 | }; 284 | 285 | unsafe { 286 | let chunk = _mm_lddqu_si128(core::mem::transmute_copy(&s)); 287 | let zeros = _mm_set1_epi8(b'0' as i8); 288 | 289 | let chunk = _mm_sub_epi16(chunk, zeros); //will wrap 290 | 291 | let zero_to_lowest = _mm_set1_epi8(-128); 292 | 293 | // 0 => -128, 1 => -127... 294 | let digits_at_lowest = _mm_add_epi8(chunk, zero_to_lowest); 295 | let upper_bound = _mm_set1_epi8(-128 + 10); 296 | let range_chk1 = _mm_cmplt_epi8(digits_at_lowest, upper_bound); 297 | let range_chk = _mm_test_all_ones(range_chk1); 298 | 299 | let is_valid = range_chk != 0; 300 | let mult = _mm_set_epi8(1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10); 301 | let chunk = _mm_maddubs_epi16(chunk, mult); 302 | 303 | let mult = _mm_set_epi16(1, 100, 1, 100, 1, 100, 1, 100); 304 | let chunk = _mm_madd_epi16(chunk, mult); 305 | 306 | let chunk = _mm_packus_epi32(chunk, chunk); 307 | let mult = _mm_set_epi16(0, 0, 0, 0, 1, 10000, 1, 10000); 308 | let chunk = _mm_madd_epi16(chunk, mult); 309 | 310 | let chunk = _mm_cvtsi128_si64(chunk) as u64; 311 | let chunk = ((chunk & 0xffffffff) * 1_0000_0000) + (chunk >> 32); 312 | 313 | if is_valid { 314 | Ok(chunk) 315 | } else { 316 | return Err(Pie { 317 | kind: IntErrorKind::InvalidDigit, 318 | }); 319 | } 320 | } 321 | } 322 | 323 | /// Parse the first 16 chars in a u8 slice as a base 10 integer. 324 | /// (Almost as good as the simd feature...) 325 | /// SAFETY: Do not call with a string length less than that. 326 | /// SAFETY: slice must be 16 byte aligned. 327 | #[cfg(not(all(target_feature = "sse2", feature = "simd")))] 328 | #[cfg(target_endian = "little")] 329 | #[inline] 330 | #[doc(hidden)] 331 | pub unsafe fn parse_16_chars(s: &[u8]) -> Result { 332 | debug_assert!(s.len() >= 16); 333 | const MASK_HI: u128 = 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0u128; 334 | const ASCII_ZEROS: u128 = 0x30303030303030303030303030303030u128; 335 | 336 | let chunk = unsafe { 337 | let ptr = s.as_ptr(); 338 | debug_assert!((ptr as usize).is_multiple_of(core::mem::size_of::())); 339 | *(ptr as *const u128) 340 | // core::ptr::read_unaligned(ptr as *const u128) 341 | ^ ASCII_ZEROS 342 | }; 343 | let chunk_og = chunk; 344 | 345 | // 1-byte mask trick (works on 8 pairs of single digits) 346 | let lower_digits = (chunk & 0x0f000f000f000f000f000f000f000f00) >> 8; 347 | let upper_digits = (chunk & 0x000f000f000f000f000f000f000f000f) * 10; 348 | let chunk = lower_digits + upper_digits; 349 | 350 | // 2-byte mask trick (works on 4 pairs of two digits) 351 | let lower_digits = (chunk & 0x00ff000000ff000000ff000000ff0000) >> 16; 352 | let upper_digits = (chunk & 0x000000ff000000ff000000ff000000ff) * 100; 353 | let chunk = lower_digits + upper_digits; 354 | 355 | // 4-byte mask trick (works on 2 pair of four digits) 356 | let lower_digits = (chunk & 0x0000ffff000000000000ffff00000000) >> 32; 357 | let upper_digits = (chunk & 0x000000000000ffff000000000000ffff) * 100_00; 358 | let chunk = lower_digits + upper_digits; 359 | 360 | let chk = chunk_og.wrapping_add(0x76767676767676767676767676767676u128); 361 | // 8-byte mask trick (works on a pair of eight digits) 362 | let lower_digits = ((chunk & 0x00000000ffffffff0000000000000000) >> 64) as u64; 363 | let upper_digits = (chunk as u64) * 100_00_00_00; //& 0x00000000ffffffff 364 | let chunk = lower_digits + upper_digits; 365 | 366 | if likely!((chunk_og & MASK_HI) | (chk & 0x80808080808080808080808080808080u128) == 0) { 367 | Ok(chunk) //u64 can guarantee to contain 19 digits. 368 | } else { 369 | Err(Pie { 370 | kind: IntErrorKind::InvalidDigit, 371 | }) 372 | } 373 | } 374 | 375 | /// Parse the first 16 chars in a u8 slice as a base 10 integer. 376 | /// (Almost as good as the simd feature...) 377 | /// SAFETY: Do not call with a string length less than that. 378 | /// SAFETY: slice must be 16 byte aligned. 379 | #[cfg(not(all(target_feature = "sse2", feature = "simd")))] 380 | #[cfg(not(target_endian = "little"))] 381 | #[inline] 382 | #[doc(hidden)] 383 | pub unsafe fn parse_16_chars(s: &[u8]) -> Result { 384 | debug_assert!(s.len() >= 16); 385 | const MASK_HI: u128 = 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0u128; 386 | const ASCII_ZEROS: u128 = 0x30303030303030303030303030303030u128; 387 | 388 | let chunk = unsafe { 389 | let ptr = s.as_ptr(); 390 | debug_assert!(ptr as usize % core::mem::size_of::() == 0); 391 | *(ptr as *const u128) 392 | // } else { 393 | // panic!("swoops16"); 394 | 395 | // core::ptr::read_unaligned(ptr as *const u128) 396 | //}) 397 | ^ ASCII_ZEROS 398 | }; 399 | let chunk_og = chunk; 400 | 401 | // 1-byte mask trick (works on 8 pairs of single digits) 402 | let lower_digits = ((chunk & 0x0f000f000f000f000f000f000f000f00) >> 8) * 10; 403 | let upper_digits = chunk & 0x000f000f000f000f000f000f000f000f; 404 | let chunk = lower_digits + upper_digits; 405 | 406 | // 2-byte mask trick (works on 4 pairs of two digits) 407 | let lower_digits = ((chunk & 0x00ff000000ff000000ff000000ff0000) >> 16) * 100; 408 | let upper_digits = chunk & 0x000000ff000000ff000000ff000000ff; 409 | let chunk = lower_digits + upper_digits; 410 | 411 | // 4-byte mask trick (works on 2 pair of four digits) 412 | let lower_digits = ((chunk & 0x0000ffff000000000000ffff00000000) >> 32) * 100_00; 413 | let upper_digits = chunk & 0x000000000000ffff000000000000ffff; 414 | let chunk = lower_digits + upper_digits; 415 | 416 | let chk = chunk_og.wrapping_add(0x76767676767676767676767676767676u128); 417 | // 8-byte mask trick (works on a pair of eight digits) 418 | let lower_digits = (((chunk & 0x00000000ffffffff0000000000000000) >> 64) as u64) * 100_00_00_00; 419 | let upper_digits = chunk as u64; //& 0x00000000ffffffff 420 | let chunk = lower_digits + upper_digits; 421 | 422 | if likely!((chunk_og & MASK_HI) | (chk & 0x80808080808080808080808080808080u128) == 0) { 423 | Ok(chunk) //u64 can guarantee to contain 19 digits. 424 | } else { 425 | Err(Pie { 426 | kind: IntErrorKind::InvalidDigit, 427 | }) 428 | } 429 | } 430 | 431 | /// Parse the first 8 chars in a u8 slice as a base 10 integer. 432 | /// SAFETY: Do not call with a string length less than that. 433 | /// SAFETY: slice must be 8 byte aligned. 434 | #[cfg(target_endian = "little")] 435 | #[inline] 436 | #[doc(hidden)] 437 | pub unsafe fn parse_8_chars(s: &[u8]) -> Result { 438 | debug_assert!(s.len() >= 8); 439 | const MASK_HI: u64 = 0xf0f0f0f0f0f0f0f0u64; 440 | const ASCII_ZEROS: u64 = 0x3030303030303030u64; 441 | let ptr = s.as_ptr(); 442 | let chunk = unsafe { 443 | debug_assert!((ptr as usize).is_multiple_of(core::mem::size_of::())); 444 | // (if ptr as usize % core::mem::size_of::() == 0 { 445 | *(ptr as *const u64) 446 | // } else { 447 | // panic!("swoops8"); 448 | 449 | // core::ptr::read_unaligned(ptr as *const u64) 450 | // }) 451 | ^ ASCII_ZEROS 452 | }; 453 | let valid = (chunk & MASK_HI) 454 | | (chunk.wrapping_add(0x7676767676767676u64) & 0x8080808080808080u64) 455 | == 0; 456 | 457 | // 1-byte mask trick (works on 4 pairs of single digits) 458 | let lower_digits = (chunk & 0x0f000f000f000f00) >> 8; 459 | let upper_digits = (chunk & 0x000f000f000f000f) * 10; //Compiler does *8 + *2 460 | let chunk = lower_digits + upper_digits; 461 | 462 | // 2-byte mask trick (works on 2 pairs of two digits) 463 | let lower_digits = (chunk & 0x00ff000000ff0000) >> 16; 464 | let upper_digits = (chunk & 0x000000ff000000ff) * 100; 465 | let chunk = lower_digits + upper_digits; 466 | 467 | // 4-byte mask trick (works on a pair of four digits) 468 | let lower_digits = ((chunk & 0x0000ffff00000000) >> 32) as u32; 469 | let upper_digits = (chunk as u32) * 10000; //10000 = 8192 + 1024 + 512 + 256+ 16 470 | //8192 + 2048 + 16 - 256 //& 0x0000ffff 471 | 472 | //We do this before the if shaving 300ps. 473 | let chunk = lower_digits + upper_digits; 474 | 475 | if likely!(valid) { 476 | Ok(chunk) //u32 can guarantee to contain 9 digits. 477 | } else { 478 | Err(Pie { 479 | kind: IntErrorKind::InvalidDigit, 480 | }) 481 | } 482 | } 483 | 484 | /// Parse the first 8 chars in a u8 slice as a base 10 integer. 485 | /// SAFETY: Do not call with a string length less than that. 486 | /// SAFETY: slice must be 8 byte aligned. 487 | #[cfg(not(target_endian = "little"))] 488 | #[inline] 489 | #[doc(hidden)] 490 | pub unsafe fn parse_8_chars(s: &[u8]) -> Result { 491 | debug_assert!(s.len() >= 8); 492 | const MASK_HI: u64 = 0xf0f0f0f0f0f0f0f0u64; 493 | const ASCII_ZEROS: u64 = 0x3030303030303030u64; 494 | 495 | let chunk = unsafe { 496 | let ptr = s.as_ptr(); 497 | debug_assert!(ptr as usize % core::mem::size_of::() == 0); 498 | // (if ptr as usize % core::mem::size_of::() == 0 { 499 | *(ptr as *const u64) 500 | // } else { 501 | // panic!("swoops8"); 502 | 503 | // core::ptr::read_unaligned(ptr as *const u64) 504 | // }) 505 | ^ ASCII_ZEROS 506 | }; 507 | 508 | let valid = (chunk & MASK_HI) 509 | | (chunk.wrapping_add(0x7676767676767676u64) & 0x8080808080808080u64) 510 | == 0; 511 | 512 | // 1-byte mask trick (works on 4 pairs of single digits) 513 | let lower_digits = ((chunk & 0x0f000f000f000f00) >> 8) * 10; //Compiler does *8 + *2 514 | let upper_digits = chunk & 0x000f000f000f000f; 515 | let chunk = lower_digits + upper_digits; 516 | 517 | // 2-byte mask trick (works on 2 pairs of two digits) 518 | let lower_digits = ((chunk & 0x00ff000000ff0000) >> 16) * 100; //TODO: decompose * 100 to shifts 519 | let upper_digits = chunk & 0x000000ff000000ff; 520 | let chunk = lower_digits + upper_digits; 521 | 522 | // 4-byte mask trick (works on a pair of four digits) 523 | let lower_digits = (((chunk & 0x0000ffff00000000) >> 32) * 10000) as u32; 524 | let upper_digits = chunk as u32; 525 | 526 | //We do this before the if shaving 300ps. 527 | let chunk = lower_digits + upper_digits; 528 | 529 | if likely!(valid) { 530 | Ok(chunk) //u32 can guarantee to contain 9 digits. 531 | } else { 532 | Err(Pie { 533 | kind: IntErrorKind::InvalidDigit, 534 | }) 535 | } 536 | } 537 | 538 | /// Parse the first 4 chars in a u8 slice as a base 10 integer. 539 | /// SAFETY: Do not call with a string length less than that. 540 | /// SAFETY: slice must be 4 byte aligned. 541 | #[cfg(target_endian = "little")] 542 | #[inline] 543 | #[doc(hidden)] 544 | pub unsafe fn parse_4_chars(s: &[u8]) -> Result { 545 | //SAFETY: 546 | debug_assert!(s.len() >= 4); 547 | 548 | const MASK_HI: u32 = 0xf0f0f0f0u32; 549 | const ASCII_ZEROS: u32 = 0x30303030u32; 550 | let ptr = s.as_ptr() as usize; 551 | let chunk1 = unsafe { 552 | debug_assert!(ptr.is_multiple_of(core::mem::size_of::())); 553 | // (if ptr % size == 0 { 554 | *(s.as_ptr() as *const u32) 555 | // } else { 556 | // panic!("swoops4"); 557 | // core::ptr::read_unaligned(ptr as *const u32) 558 | // }) 559 | ^ ASCII_ZEROS 560 | }; 561 | // 1-byte mask trick (works on 4 pairs of single digits) 562 | let lower_digits = (chunk1 & 0x0f000f00) >> 8; // => 0x00f000f0 563 | 564 | let sum = chunk1.wrapping_add(0x76767676u32) & 0x80808080u32; 565 | 566 | let chunk = lower_digits + (chunk1 & 0x000f000f) * 10; 567 | 568 | let masked = chunk as u16; // & 0x00ff; 569 | //Next line should be: 570 | // let cond = (chunk1 & MASK_HI) | sum == 0; 571 | // but hit a wasm bug: https://github.com/rust-lang/rust/issues/85580 572 | let cond = (chunk1 & MASK_HI) == 0 && sum == 0; 573 | 574 | // Multiply by 100 via shifts 575 | let m1 = masked << 6; 576 | let m2 = masked << 5; 577 | let m3 = masked << 2; 578 | 579 | let r = ((chunk & 0x00ff0000) >> 16) as u16; 580 | 581 | // 2-byte mask trick (works on 2 pairs of two digits) 582 | let chunk = r + m1 + m2 + m3; 583 | 584 | if likely!(cond) { 585 | Ok(chunk) //u16 can guarantee to hold 4 digits 586 | } else { 587 | Err(Pie { 588 | kind: IntErrorKind::InvalidDigit, 589 | }) 590 | } 591 | } 592 | 593 | /// Big-endien: test with: 594 | /// ``` 595 | /// cargo miri test --target mips64-unknown-linux-gnuabi64 596 | /// ``` 597 | /// E.g. "1234" is represented as: 598 | /// 0x31323334 599 | /// 600 | /// For big endien it's in the right order. 601 | /// SAFETY: minimum of string len 4. 602 | /// SAFETY: slice must be 8 byte aligned. 603 | #[cfg(not(target_endian = "little"))] 604 | #[inline] 605 | #[doc(hidden)] 606 | pub unsafe fn parse_4_chars(s: &[u8]) -> Result { 607 | //SAFETY: 608 | debug_assert!(s.len() >= 4); 609 | 610 | const MASK_HI: u32 = 0xf0f0f0f0u32; 611 | const ASCII_ZEROS: u32 = 0x30303030u32; 612 | 613 | let chunk1 = unsafe { 614 | let ptr = s.as_ptr(); 615 | debug_assert!(ptr as usize % core::mem::size_of::() == 0); 616 | //(if ptr as usize % core::mem::size_of::() == 0 { 617 | *(ptr as *const u32) 618 | // } else { 619 | // panic!("swoops4"); 620 | // core::ptr::read_unaligned(ptr as *const u32) 621 | // }) 622 | ^ ASCII_ZEROS 623 | }; 624 | 625 | // 1-byte mask trick (works on 4 pairs of single digits) 626 | let tens = (chunk1 & 0x0f000f00) >> 8; // => 0x00f000f0 627 | 628 | let sum = chunk1.wrapping_add(0x76767676u32) & 0x80808080u32; 629 | 630 | let units = chunk1 & 0x000f000f; 631 | let chunk = tens * 10 + (units); 632 | 633 | let masked = chunk; // & 0x00ff; 634 | //Next line should be: 635 | // let cond = (chunk1 & MASK_HI) | sum == 0; 636 | // but hit a wasm bug: https://github.com/rust-lang/rust/issues/85580 637 | let cond = (chunk1 & MASK_HI) == 0 && sum == 0; 638 | 639 | // Multiply by 100! 640 | let m1 = (masked & 0x00ff0000) >> 10; //16 - 6 641 | let m2 = (masked & 0x00ff0000) >> 11; //16 - 5 642 | let m3 = (masked & 0x00ff0000) >> 14; //16 - 2 643 | 644 | let r = (chunk & 0x000000ff) as u16; 645 | 646 | // 2-byte mask trick (works on 2 pairs of two digits) 647 | let chunk = r + (m1 + m2 + m3) as u16; 648 | 649 | if likely!(cond) { 650 | Ok(chunk) //u16 can guarantee to hold 4 digits 651 | } else { 652 | Err(Pie { 653 | kind: IntErrorKind::InvalidDigit, 654 | }) 655 | } 656 | } 657 | 658 | /// Parse the first 2 chars in a u8 slice as a base 10 integer. 659 | /// (Returning u16 rather than u8 as faster.) 660 | /// SAFETY: Do not call with a string length less than that. 661 | /// SAFETY: slice must be 2 byte aligned. 662 | #[cfg(target_endian = "little")] 663 | #[inline] 664 | #[doc(hidden)] 665 | pub unsafe fn parse_2_chars(s: &[u8]) -> Result { 666 | //SAFETY: 667 | debug_assert!(s.len() >= 2); 668 | debug_assert_eq!(s.as_ptr() as usize % core::mem::size_of::(), 0); 669 | let chunk = unsafe { *(s.as_ptr() as *const u16) ^ 0x3030u16 }; 670 | //Early add 671 | let ch = chunk.wrapping_add(0x7676u16); 672 | //Early calc result before use 673 | let res = ((chunk & 0x000f) << 1) + ((chunk & 0x000f) << 3) + ((chunk & 0x0f00) >> 8); 674 | 675 | if likely!((chunk & 0xf0f0u16) | (ch & 0x8080u16) == 0) { 676 | Ok(res) 677 | } else { 678 | Err(Pie { 679 | kind: IntErrorKind::InvalidDigit, 680 | }) 681 | } 682 | } 683 | 684 | /// SAFETY: Do not call with a string length less than that. 685 | /// SAFETY: slice must be 2 byte aligned. 686 | #[cfg(not(target_endian = "little"))] 687 | #[inline] 688 | #[doc(hidden)] 689 | pub unsafe fn parse_2_chars(s: &[u8]) -> Result { 690 | debug_assert!(s.len() >= 2); 691 | 692 | let chunk = unsafe { 693 | let ptr = s.as_ptr(); 694 | debug_assert!(ptr as usize % core::mem::size_of::() == 0); 695 | //(if ptr as usize % core::mem::size_of::() == 0 { 696 | *(ptr as *const u16) 697 | // } else { 698 | // panic!("swoops2"); 699 | // core::ptr::read_unaligned(ptr as *const u16) 700 | // }) 701 | ^ 0x3030u16 702 | }; 703 | //Early add 704 | let ch = chunk.wrapping_add(0x7676u16); 705 | //Early calc result before use 706 | //Shift >> 8 is consolidated with *10 shifts: *10 = << 3 + << 1 707 | let res = (chunk & 0x000f) + ((chunk & 0x0f00) >> 5) + ((chunk & 0x0f00) >> 7); 708 | 709 | if likely!((chunk & 0xf0f0u16) | (ch & 0x8080u16) == 0) { 710 | Ok(res) 711 | } else { 712 | Err(Pie { 713 | kind: IntErrorKind::InvalidDigit, 714 | }) 715 | } 716 | } 717 | 718 | #[cfg(test)] 719 | mod tests { 720 | use super::*; 721 | use crate::parse::FromStrRadixHelper; 722 | use heapless::Vec; 723 | use numtoa::NumToA; 724 | use paste::paste; 725 | 726 | // #[wasm_bindgen_test] 727 | // #[test] 728 | // fn test_uu128_specific() { 729 | // let s = "+000123"; 730 | // let p: Result = s.parse().map_err(|_| ()); 731 | // assert_eq!( 732 | // p, 733 | // super::parse::(s.as_bytes()).map_err(|_| ()), 734 | // "fail to parse: '{}'", 735 | // &s 736 | // ); 737 | // } 738 | 739 | /// This test is ignored because it can only really call the fn when 740 | /// the alignment is right and we're not currently setting up the alignment 741 | /// so the test is not safe. 742 | #[test] 743 | #[ignore] 744 | fn test_bench32() { 745 | // A way to dbg! in no_std: 746 | // env_logger::init(); 747 | 748 | let s = "34028236692093846346337460743176821145"; 749 | assert_eq!( 750 | Ok(34028236692093846346337460743176_u128), 751 | unsafe { super::parse_32_chars(s.as_bytes()).map_err(|_| ()) }, 752 | "fail to parse: '{}'", 753 | &s 754 | ); 755 | } 756 | 757 | #[test] 758 | fn test_uu12ddd8_specific() { 759 | // 340282366920938463463374607431768211455 760 | //let s = "34028236692093846346337460743176821145"; 761 | let s = "01234567890123456789012345678901234567"; 762 | let p: Result = s.parse().map_err(|_| ()); 763 | //assert_matches!(p, Ok(_)); 764 | assert_eq!( 765 | p, 766 | super::parse::(s.as_bytes()).map_err(|_| ()), 767 | "fail to parse: '{}'", 768 | &s 769 | ); 770 | } 771 | 772 | macro_rules! gen_tests { 773 | ($target_type:ty, $min:expr, $max:expr, $step: expr, $max_chars: literal,$postfix: literal, $specific: literal) => { 774 | paste! { 775 | #[wasm_bindgen_test] 776 | #[test] 777 | fn []() { 778 | let s = $specific; 779 | let p: Result<$target_type, ()> = s.parse().map_err(|_| ()); 780 | assert_eq!(p, []::<$target_type>(s.as_bytes()).map_err(|_| ()), "fail to parse: '{}'", &s); 781 | } 782 | 783 | #[wasm_bindgen_test] 784 | #[test] 785 | fn []() { 786 | let mut vec = Vec::<_, 42>::new(); 787 | for &ascii in [b':', b'/'].iter() { 788 | for i in 1..$max_chars { 789 | vec.clear(); 790 | for _ in 0..i { 791 | vec.push(b'1').unwrap(); 792 | } 793 | for j in 1..i { 794 | let mut v = vec.clone(); 795 | v[j] = ascii; 796 | let s = unsafe { core::str::from_utf8_unchecked(&v[..]) }; 797 | assert_eq!(Err(ParseIntErrorPublic{kind:IntErrorKind::InvalidDigit}), []::<$target_type>(s.as_bytes()), "parsing `{}`", s); 798 | } 799 | } 800 | } 801 | } 802 | 803 | #[wasm_bindgen_test] 804 | #[test] 805 | fn []() { 806 | let mut s = [0u8; 42]; 807 | let ss = ($target_type::MAX as $target_type).numtoa(10, &mut s); 808 | let len = ss.len(); 809 | s[len] = b'1'; 810 | let s = unsafe { core::str::from_utf8_unchecked(&s[..len + 1]) }; 811 | assert_eq!( 812 | Err(()), 813 | []::<$target_type>(s.as_bytes()).map_err(|_|()), 814 | " when parsing '{}'", 815 | &s 816 | ); 817 | } 818 | 819 | #[wasm_bindgen_test] 820 | #[test] 821 | fn []() { 822 | assert_eq!( 823 | Err(Pie { 824 | kind: IntErrorKind::Empty 825 | }), 826 | []::<$target_type>("".as_bytes()) 827 | ); 828 | } 829 | 830 | //#[wasm_bindgen_test] step too small for wasm 831 | #[cfg_attr(miri, ignore)] 832 | #[test] 833 | fn []() { 834 | let mut s = [0u8; 42]; 835 | 836 | for i in ($min..$max as $target_type).step_by($step) { 837 | let s = unsafe { core::str::from_utf8_unchecked(i.numtoa(10, &mut s)) }; 838 | let p: Result<$target_type, ()> = s.parse().map_err(|_| ()); 839 | assert_eq!(p, []::<$target_type>(s.as_bytes()).map_err(|_| ()), "fail to parse: '{}'", &s); 840 | } 841 | } 842 | 843 | #[cfg_attr(miri, ignore)] 844 | #[test] 845 | fn []() { 846 | use rand::prelude::*; 847 | let mut s = [0u8; 42]; 848 | let mut rng = rand::rng(); 849 | 850 | for _ in 1..100_000 { 851 | let i = rng.random::<$target_type>(); 852 | let s = unsafe { core::str::from_utf8_unchecked(i.numtoa(10, &mut s)) }; 853 | let p: Result<$target_type, ()> = s.parse().map_err(|_| ()); 854 | assert_eq!(p, []::<$target_type>(s.as_bytes()).map_err(|_| ()), "fail to parse: '{}'", &s); 855 | } 856 | } 857 | 858 | //#[wasm_bindgen_test] step too small for wasm 859 | #[cfg_attr(miri, ignore)] 860 | #[test] 861 | fn []() { 862 | for i in ($min..$max as $target_type).step_by($step) { 863 | let mut s = [0u8; 42]; 864 | s[0] = b'+'; 865 | let ss = i.numtoa(10, &mut s[1..]); 866 | let len = ss.len(); 867 | let s = unsafe { core::str::from_utf8_unchecked(&s[0..len+1]) }; 868 | let p: Result<$target_type, ()> = s.parse().map_err(|_| ()); 869 | assert_eq!(p, []::<$target_type>(s.as_bytes()).map_err(|_| ()), "fail to parse: '{}'", &s); 870 | } 871 | } 872 | 873 | #[wasm_bindgen_test] 874 | #[test] 875 | fn []() { 876 | let mut s = [0u8; 50]; 877 | s[s.len() - 1] = b'+'; 878 | let s = unsafe { core::str::from_utf8_unchecked(&s) }; 879 | let p: Result<$target_type, ()> = s.parse().map_err(|_| ()); 880 | assert_eq!(p, []::<$target_type>(s.as_bytes()).map_err(|_| ()), "fail to parse: '{:?}'", &s); 881 | } 882 | 883 | #[wasm_bindgen_test] 884 | #[test] 885 | fn []() { 886 | //let i = $max; 887 | let s = [0u8; 142]; 888 | let s = unsafe { core::str::from_utf8_unchecked(&s) }; 889 | let p: Result<$target_type, ()> = s.parse().map_err(|_| ()); 890 | assert_eq!(p, []::<$target_type>(s.as_bytes()).map_err(|_| ()), "fail to parse: '{}'", &s); 891 | } 892 | 893 | #[wasm_bindgen_test] 894 | #[test] 895 | fn []() { 896 | let mut s = [0u8; 142]; 897 | let ss = ($target_type::MAX as $target_type).numtoa(10, &mut s[100..]); 898 | let len = ss.len(); 899 | 900 | let s = unsafe { core::str::from_utf8_unchecked(&s[..100 + len]) }; 901 | let p: Result<$target_type, ()> = s.parse().map_err(|_| ()); 902 | assert_eq!(p, []::<$target_type>(s.as_bytes()).map_err(|_| ()), "fail to parse: '{}'", &s); 903 | } 904 | } 905 | } 906 | } 907 | 908 | gen_tests!(u8, u8::MIN, u8::MAX, 1, 3, "", "1"); 909 | gen_tests!(u8, u8::MIN, u8::MAX, 1, 3, "_challenger", "+200"); 910 | 911 | gen_tests!(i8, i8::MIN, i8::MAX, 1, 3, "", "1"); 912 | gen_tests!(i8, i8::MIN, i8::MAX, 1, 3, "_challenger", "-99"); 913 | 914 | gen_tests!(u16, u16::MIN, u16::MAX, 1, 5, "", "1"); 915 | gen_tests!(u16, u16::MIN, u16::MAX, 1, 5, "_challenger", "1"); 916 | 917 | gen_tests!(i16, i16::MIN, i16::MAX, 1, 5, "", "1"); 918 | gen_tests!(i16, i16::MIN, i16::MAX, 1, 5, "_challenger", "1"); 919 | 920 | gen_tests!(u32, u32::MIN, u32::MAX, 10_301, 10, "", "1"); 921 | gen_tests!( 922 | u32, 923 | u32::MIN, 924 | u32::MAX, 925 | 10_301, 926 | 10, 927 | "_challenger", 928 | "4294967295" 929 | ); 930 | 931 | gen_tests!(i32, i32::MIN, i32::MAX, 10_301, 10, "", "-2147483648"); 932 | gen_tests!(i32, i32::MIN, i32::MAX, 10_301, 10, "_challenger", "1"); 933 | 934 | #[cfg(target_pointer_width = "16")] 935 | const LARGE_STEP: usize = 12345; 936 | 937 | #[cfg(target_pointer_width = "32")] 938 | const LARGE_STEP: usize = usize::MAX; 939 | 940 | #[cfg(target_pointer_width = "64")] 941 | const LARGE_STEP: usize = 100_301_000_000_000; 942 | 943 | gen_tests!( 944 | u64, 945 | u64::MIN, 946 | u64::MAX, 947 | LARGE_STEP, 948 | 20, 949 | "", 950 | "0000000000000000018446744073709551615" 951 | ); 952 | gen_tests!( 953 | u64, 954 | u64::MIN, 955 | u64::MAX, 956 | LARGE_STEP, 957 | 20, 958 | "_challenger", 959 | "10000009700000000000" 960 | ); 961 | 962 | gen_tests!( 963 | i64, 964 | i64::MIN, 965 | i64::MAX, 966 | LARGE_STEP, 967 | 19, 968 | "", 969 | "-999993949854775808" 970 | ); 971 | 972 | gen_tests!(i64, i64::MIN, i64::MAX, LARGE_STEP, 19, "_challenger", "1"); 973 | 974 | gen_tests!( 975 | u128, 976 | u64::MIN as u128, 977 | u64::MAX, 978 | LARGE_STEP, 979 | 39, 980 | "", 981 | "10000009700000000000" 982 | ); 983 | 984 | gen_tests!( 985 | u128, 986 | u64::MIN as u128, 987 | u64::MAX, 988 | LARGE_STEP, 989 | 39, 990 | "_challenger", 991 | "123456789012345678901234567890123456789" 992 | ); 993 | 994 | gen_tests!( 995 | i128, 996 | u64::MIN as i128, 997 | u64::MAX, 998 | LARGE_STEP, 999 | 39, 1000 | "", 1001 | "1701411834604692317316873037158841057271" // "1:11111111111111" 1002 | ); 1003 | 1004 | gen_tests!( 1005 | i128, 1006 | u64::MIN as i128, 1007 | u64::MAX, 1008 | LARGE_STEP, 1009 | 39, 1010 | "_challenger", 1011 | "123456789012345678901234567890123456789" 1012 | ); 1013 | 1014 | use wasm_bindgen_test::wasm_bindgen_test; 1015 | 1016 | #[wasm_bindgen_test] 1017 | #[test] 1018 | fn test_fuzz1() { 1019 | // needed checked add when checking digits in range. 1020 | check::([50, 35, 43, 120]); 1021 | } 1022 | 1023 | #[wasm_bindgen_test] 1024 | #[test] 1025 | fn test_fuzz2() { 1026 | // Too long is defined by std as invalid digit error rather than overflow. 1027 | check::([48, 48, 54, 54, 49, 54, 56, 57, 54, 49, 51]); 1028 | } 1029 | 1030 | #[wasm_bindgen_test] 1031 | #[test] 1032 | fn test_fuzz3() { 1033 | //"00661689613" std can deal with any number of leading zeros 1034 | // as it keeps multiplying them by 10... 1035 | check::([54, 48, 48, 48, 54, 48, 54, 48, 54, 48, 54]); 1036 | } 1037 | 1038 | #[wasm_bindgen_test] 1039 | #[test] 1040 | fn test_fuzz4() { 1041 | //leading zeros then plus: "0000+6600660" 1042 | check::([48, 48, 48, 48, 43, 54, 54, 48, 48, 54, 54, 48]); 1043 | } 1044 | #[wasm_bindgen_test] 1045 | #[test] 1046 | fn test_fuzz5() { 1047 | //leading zeros then plus: "0000+6600660" 1048 | check::([ 1049 | 43, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 1050 | 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 1051 | 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 1052 | 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 1053 | 48, 48, 48, 48, 48, 48, 1054 | ]); 1055 | } 1056 | #[wasm_bindgen_test] 1057 | #[test] 1058 | fn test_fuzz6() { 1059 | check::([ 1060 | 43, 49, 43, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 1061 | ]); 1062 | } 1063 | 1064 | #[wasm_bindgen_test] 1065 | #[test] 1066 | fn test_fuzz7() { 1067 | check::([45, 57, 166]); 1068 | } 1069 | 1070 | #[wasm_bindgen_test] 1071 | #[test] 1072 | fn test_fuzz8() { 1073 | check::([45, 48, 48, 48, 48, 176]); 1074 | } 1075 | 1076 | #[wasm_bindgen_test] 1077 | #[test] 1078 | fn test_fuzz9() { 1079 | check::([45, 49, 170]); 1080 | } 1081 | 1082 | fn check(data: [u8; N]) 1083 | where 1084 | T: FromStrRadixHelper, 1085 | T: core::str::FromStr, 1086 | T: core::fmt::Debug, 1087 | { 1088 | let mut s = heapless::String::::new(); 1089 | for c in &data { 1090 | if (*c as char).len_utf8() != 1 { 1091 | return; 1092 | } 1093 | s.push(*c as char).unwrap(); 1094 | } 1095 | //if let Ok(s) = heapless::String::from_utf8(data.to_vec()) { 1096 | let spec: Result = s.parse(); 1097 | let expected = spec.map_err(|_| ()); 1098 | assert_eq!(expected, parse::(&data).map_err(|_| ())); 1099 | // let expected = spec.map_err(|e| e.kind().clone()); 1100 | // assert_eq!(expected, parse_u32(&data).map_err(|e| e.kind)); 1101 | // } else { 1102 | // //just make sure doesn't panic: 1103 | // let _ = parse::(&data); 1104 | // } 1105 | } 1106 | } 1107 | --------------------------------------------------------------------------------