lexical-parse-integer-1.0.5/.cargo_vcs_info.json0000644000000001630000000000100152230ustar { "git": { "sha1": "84c1c497edd80ef1f8256be58c44efcfe266fcb8" }, "path_in_vcs": "lexical-parse-integer" }lexical-parse-integer-1.0.5/CODE_OF_CONDUCT.md000064400000000000000000000230251046102023000166140ustar 00000000000000# Code of Conduct ## When Something Happens If you see a Code of Conduct violation, follow these steps: 1. Let the person know that what they did is not appropriate and ask them to stop and/or edit their message(s) or commits. 2. That person should immediately stop the behavior and correct the issue. 3. If this doesn’t happen, or if you're uncomfortable speaking up, [contact the maintainers](#contacting-maintainers). 4. As soon as available, a maintainer will look into the issue, and take [further action (see below)](#further-enforcement), starting with a warning, then temporary block, then long-term repo or organization ban. When reporting, please include any relevant details, links, screenshots, context, or other information that may be used to better understand and resolve the situation. **The maintainer team will prioritize the well-being and comfort of the recipients of the violation over the comfort of the violator.** See [some examples below](#enforcement-examples). ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers of this project pledge to making participation in our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, technical preferences, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language. - Being respectful of differing viewpoints and experiences. - Gracefully accepting constructive feedback. - Focusing on what is best for the community. - Showing empathy and kindness towards other community members. - Encouraging and raising up your peers in the project so you can all bask in hacks and glory. Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery and unwelcome sexual attention or advances, including when simulated online. The only exception to sexual topics is channels/spaces specifically for topics of sexual identity. - Casual mention of slavery or indentured servitude and/or false comparisons of one's occupation or situation to slavery. Please consider using or asking about alternate terminology when referring to such metaphors in technology. - Making light of/making mocking comments about trigger warnings and content warnings. - Trolling, insulting/derogatory comments, and personal or political attacks. - Public or private harassment, deliberate intimidation, or threats. - Publishing others' private information, such as a physical or electronic address, without explicit permission. This includes any sort of "outing" of any aspect of someone's identity without their consent. - Publishing private screenshots or quotes of interactions in the context of this project without all quoted users' *explicit* consent. - Publishing of private communication that doesn't have to do with reporting harrassment. - Any of the above even when [presented as "ironic" or "joking"](https://en.wikipedia.org/wiki/Hipster_racism). - Any attempt to present "reverse-ism" versions of the above as violations. Examples of reverse-isms are "reverse racism", "reverse sexism", "heterophobia", and "cisphobia". - Unsolicited explanations under the assumption that someone doesn't already know it. Ask before you teach! Don't assume what people's knowledge gaps are. - [Feigning or exaggerating surprise](https://www.recurse.com/manual#no-feigned-surprise) when someone admits to not knowing something. - "[Well-actuallies](https://www.recurse.com/manual#no-well-actuallys)" - Other conduct which could reasonably be considered inappropriate in a professional or community setting. ## Scope This Code of Conduct applies both within spaces involving this project and in other spaces involving community members. This includes the repository, its Pull Requests and Issue tracker, private email communications in the context of the project, and any events where members of the project are participating, as well as adjacent communities and venues affecting the project's members. Depending on the violation, the maintainers may decide that violations of this code of conduct that have happened outside of the scope of the community may deem an individual unwelcome, and take appropriate action to maintain the comfort and safety of its members. ### Other Community Standards As a project on GitHub, this project is additionally covered by the [GitHub Community Guidelines](https://help.github.com/articles/github-community-guidelines/). Enforcement of those guidelines after violations overlapping with the above are the responsibility of the entities, and enforcement may happen in any or all of the services/communities. ## Maintainer Enforcement Process Once the maintainers get involved, they will follow a documented series of steps and do their best to preserve the well-being of project members. This section covers actual concrete steps. ### Contacting Maintainers You may get in touch with the maintainer team through any of the following methods: Through email: ahuszagh@gmail.com (Alex Huszagh) ### Further Enforcement If you've already followed the [initial enforcement steps](#maintainer-enforcement-process), these are the steps maintainers will take for further enforcement, as needed: 1. Repeat the request to stop. 2. If the person doubles down, they will have offending messages removed or edited by a maintainers given an official warning. The PR or Issue may be locked. 3. If the behavior continues or is repeated later, the person will be blocked from participating for 24 hours. 4. If the behavior continues or is repeated after the temporary block, a long-term (6-12mo) ban will be used. On top of this, maintainers may remove any offending messages, images, contributions, etc, as they deem necessary. Maintainers reserve full rights to skip any of these steps, at their discretion, if the violation is considered to be a serious and/or immediate threat to the health and well-being of members of the community. These include any threats, serious physical or verbal attacks, and other such behavior that would be completely unacceptable in any social setting that puts our members at risk. Members expelled from events or venues with any sort of paid attendance will not be refunded. ### Who Watches the Watchers? Maintainers and other leaders who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. These may include anything from removal from the maintainer team to a permanent ban from the community. Additionally, as a project hosted on both GitHub and npm, [their own Codes of Conducts may be applied against maintainers of this project](#other-community-standards), externally of this project's procedures. ### Enforcement Examples #### The Best Case The vast majority of situations work out like this. This interaction is common, and generally positive. > Alex: "Yeah I used X and it was really crazy!" > Patt (not a maintainer): "Hey, could you not use that word? What about 'ridiculous' instead?" > Alex: "oh sorry, sure." -> edits old comment to say "it was really confusing!" #### The Maintainer Case Sometimes, though, you need to get maintainers involved. Maintainers will do their best to resolve conflicts, but people who were harmed by something **will take priority**. > Patt: "Honestly, sometimes I just really hate using $library and anyone who uses it probably sucks at their job." > Alex: "Whoa there, could you dial it back a bit? There's a CoC thing about attacking folks' tech use like that." > Patt: "I'm not attacking anyone, what's your problem?" > Alex: "@maintainers hey uh. Can someone look at this issue? Patt is getting a bit aggro. I tried to nudge them about it, but nope." > KeeperOfCommitBits: (on issue) "Hey Patt, maintainer here. Could you tone it down? This sort of attack is really not okay in this space." > Patt: "Leave me alone I haven't said anything bad wtf is wrong with you." > KeeperOfCommitBits: (deletes user's comment), "@patt I mean it. Please refer to the CoC over at (URL to this CoC) if you have questions, but you can consider this an actual warning. I'd appreciate it if you reworded your messages in this thread, since they made folks there uncomfortable. Let's try and be kind, yeah?" > Patt: "@keeperofbits Okay sorry. I'm just frustrated and I'm kinda burnt out and I guess I got carried away. I'll DM Alex a note apologizing and edit my messages. Sorry for the trouble." > KeeperOfCommitBits: "@patt Thanks for that. I hear you on the stress. Burnout sucks :/. Have a good one!" #### The Nope Case > PepeTheFrog🐸: "Hi, I am a literal actual nazi and I think white supremacists are quite fashionable." > Patt: "NOOOOPE. OH NOPE NOPE." > Alex: "JFC NO. NOPE. @keeperofbits NOPE NOPE LOOK HERE" > KeeperOfCommitBits: "👀 Nope. NOPE NOPE NOPE. 🔥" > PepeTheFrog🐸 has been banned from all organization or user repositories belonging to KeeperOfCommitBits. ## Attribution This Code of Conduct was generated using [WeAllJS Code of Conduct Generator](https://npm.im/weallbehave), which is based on the [WeAllJS Code of Conduct](https://wealljs.org/code-of-conduct), which is itself based on [Contributor Covenant](http://contributor-covenant.org), version 1.4, available at [http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4), and the LGBTQ in Technology Slack [Code of Conduct](http://lgbtq.technology/coc.html). lexical-parse-integer-1.0.5/Cargo.toml0000644000000041440000000000100132240ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.63.0" name = "lexical-parse-integer" version = "1.0.5" authors = ["Alex Huszagh "] build = false exclude = [ "assets/*", "docs/*", "etc/*", "cargo-timing*.html", ] autobins = false autoexamples = false autotests = false autobenches = false description = "Efficient parsing of integers from strings." readme = "README.md" keywords = [ "parsing", "lexical", "no_std", ] categories = [ "parsing", "no-std", ] license = "MIT/Apache-2.0" repository = "https://github.com/Alexhuszagh/rust-lexical" [package.metadata.docs.rs] features = [ "radix", "format", ] [lib] name = "lexical_parse_integer" path = "src/lib.rs" [[test]] name = "algorithm_tests" path = "tests/algorithm_tests.rs" [[test]] name = "api_tests" path = "tests/api_tests.rs" [[test]] name = "issue_91_tests" path = "tests/issue_91_tests.rs" [[test]] name = "issue_96_tests" path = "tests/issue_96_tests.rs" [[test]] name = "issue_98_tests" path = "tests/issue_98_tests.rs" [[test]] name = "options_tests" path = "tests/options_tests.rs" [[test]] name = "partial_tests" path = "tests/partial_tests.rs" [[test]] name = "util" path = "tests/util.rs" [dependencies.lexical-util] version = "1.0.5" features = ["parse-integers"] default-features = false [dependencies.static_assertions] version = "1" [dev-dependencies.proptest] version = ">=1.5.0" [features] compact = ["lexical-util/compact"] default = ["std"] format = ["lexical-util/format"] lint = ["lexical-util/lint"] power-of-two = ["lexical-util/power-of-two"] radix = [ "lexical-util/radix", "power-of-two", ] std = ["lexical-util/std"] lexical-parse-integer-1.0.5/Cargo.toml.orig000064400000000000000000000033071046102023000167050ustar 00000000000000[package] authors = ["Alex Huszagh "] autobenches = false categories = ["parsing", "no-std"] description = "Efficient parsing of integers from strings." edition = "2021" keywords = ["parsing", "lexical", "no_std"] license = "MIT/Apache-2.0" name = "lexical-parse-integer" readme = "README.md" repository = "https://github.com/Alexhuszagh/rust-lexical" version = "1.0.5" rust-version = "1.63.0" exclude = [ "assets/*", "docs/*", "etc/*", "cargo-timing*.html" ] [dependencies] static_assertions = "1" [dependencies.lexical-util] version = "1.0.5" path = "../lexical-util" default-features = false features = ["parse-integers"] [dev-dependencies] # FIXME: Replace back to "1.0.4" once the PR is merged. # There's an issue in quickcheck due to an infinitely repeating shrinker. # Issue: https://github.com/BurntSushi/quickcheck/issues/295 # Fix: https://github.com/BurntSushi/quickcheck/pull/296 quickcheck = { git = "https://github.com/Alexhuszagh/quickcheck/", branch = "i32min-shrink-bound-legacy" } proptest = ">=1.5.0" [features] default = ["std"] # Use the standard library. std = ["lexical-util/std"] # Add support for parsing power-of-two integer strings. power-of-two = ["lexical-util/power-of-two"] # Add support for parsing non-decimal integer strings. radix = ["lexical-util/radix", "power-of-two"] # Add support for parsing custom integer formats. format = ["lexical-util/format"] # Reduce code size at the cost of performance. compact = ["lexical-util/compact"] # INTERNAL ONLY # ------------- # Internal only features. These are not meant to be used directly. # Enable the lint checks. lint = ["lexical-util/lint"] [package.metadata.docs.rs] features = ["radix", "format"] lexical-parse-integer-1.0.5/LICENSE-APACHE000064400000000000000000000251421046102023000157430ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. lexical-parse-integer-1.0.5/LICENSE-MIT000064400000000000000000000017771046102023000154630ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lexical-parse-integer-1.0.5/LICENSE.md000064400000000000000000000454631046102023000154330ustar 00000000000000# Licensing Lexical is dual licensed under the Apache 2.0 license as well as the MIT license. See the LICENCE-MIT and the LICENCE-APACHE files for the licenses. Other licensing terms may apply, as described in depth below for various features and functionality. All assume use of `lexical` or `lexical-core`. ## `write-floats, not(compact)` `lexical-write-float/src/algorithm.rs` is a direct port of the reference C++ implementation of Dragonbox, found [here](https://github.com/jk-jeon/dragonbox/). This code (used if the `write-floats` feature is enabled and the `compact` feature is disabled) is subject to a [Boost Software License](https://github.com/jk-jeon/dragonbox/blob/71993f55067a89f4b4e27591605e21521f5c61be/LICENSE-Boost) and a modified [Apache2 license](https://github.com/jk-jeon/dragonbox/blob/71993f55067a89f4b4e27591605e21521f5c61be/LICENSE-Apache2-LLVM), shown in the [Boost Software License](#boost-software-license) and [Apache2 With LLVM Exceptions](#apache2-with-llvm-exceptions) sections below. ## `write-floats, compact` `lexical-write-float/src/compact.rs` is a direct port of a C++ implementation of the Grisu algorithm, found [here](https://github.com/night-shift/fpconv/). This code (used if both the `write-floats` and `compact` features are enabled) is subject to a [MIT License](https://github.com/night-shift/fpconv/blob/dfeb7e938fb85fb5eca130b84f856705ced75012/license), shown in the [fpconv License](#fpconv-license) section below. ## `write-floats, radix` `lexical-write-float/src/radix.rs` is adapted from the V8 implementation found [here](). This code (used if both the `parse-floats` and `radix` features are enabled) is subject to a [3-clause BSD license](https://github.com/v8/v8/blob/f80bfeaf0792652bfbc1f174d5a7b8ab8bc0cbbd/LICENSE.v8), shown in the [V8 License](#v8-license) section below. ## `parse-floats, compact` `lexical-parse-float/src/bellerophon.rs` is loosely based off the Golang implementation, found [here](https://github.com/golang/go/blob/b10849fbb97a2244c086991b4623ae9f32c212d0/src/strconv/extfloat.go). This code (used if both the `parse-floats` and `compact` features are enabled) is subject to a [3-clause BSD license](https://github.com/golang/go/blob/b10849fbb97a2244c086991b4623ae9f32c212d0/LICENSE), shown in the [Go License](#go-license) section below. # License Terms This contains complete copies of the licensing terms for the feature-dependent code described above. ## Go License Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## Boost Software License Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ## Apache2 With LLVM Exceptions _Version 2.0, January 2004_ _<>_ ### Terms and Conditions for use, reproduction, and distribution #### 1. Definitions “License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. “Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. “Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means **(i)** the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the outstanding shares, or **(iii)** beneficial ownership of such entity. “You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License. “Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. “Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. “Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). “Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. “Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.” “Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. #### 2. Grant of Copyright License Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. #### 3. Grant of Patent License Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. #### 4. Redistribution You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: * **(a)** You must give any other recipients of the Work or Derivative Works a copy of this License; and * **(b)** You must cause any modified files to carry prominent notices stating that You changed the files; and * **(c)** You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and * **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. #### 5. Submission of Contributions Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. #### 6. Trademarks This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. #### 7. Disclaimer of Warranty Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. #### 8. Limitation of Liability In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. #### 9. Accepting Warranty or Additional Liability While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. _END OF TERMS AND CONDITIONS_ ### APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets `[]` replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same “printed page” as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ### LLVM Exceptions to the Apache 2.0 License As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into an Object form of such source code, you may redistribute such embedded portions in such Object form without complying with the conditions of Sections 4(a), 4(b) and 4(d) of the License. In addition, if you combine or link compiled forms of this Software with software that is licensed under the GPLv2 ("Combined Software") and if a court of competent jurisdiction determines that the patent provision (Section 3), the indemnity provision (Section 9) or other Section of the License conflicts with the conditions of the GPLv2, you may retroactively and prospectively choose to deem waived or otherwise exclude such Section(s) of the License, but only in their entirety and only with respect to the Combined Software. ## V8 License Copyright 2014, the V8 project authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## fpconv License The MIT License Copyright (c) 2013 Andreas Samoljuk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lexical-parse-integer-1.0.5/README.md000064400000000000000000000523741046102023000153050ustar 00000000000000# lexical High-performance numeric conversion routines for use in a `no_std` environment. This does not depend on any standard library features, nor a system allocator. Comprehensive benchmarks can be found at [lexical-benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks). **Similar Projects** If you want a minimal, performant float parser, recent versions of the Rust standard library should be [sufficient](https://github.com/rust-lang/rust/pull/86761). For high-performance integer formatters, look at [itoa](https://docs.rs/itoa/latest/itoa/). The [metrics](#metrics) section contains a detailed comparison of various crates and their performance in comparison to lexical. Lexical is the currently fastest Rust number formatter and parser, and is tested against: - [itoa](https://crates.io/crates/itoa) - [dtoa](https://crates.io/crates/dtoa) - [ryu](https://crates.io/crates/ryu) - Rust core library **Table of Contents** - [Getting Started](#getting-started) - [Partial/Complete Parsers](#partialcomplete-parsers) - [no_std](#no_std) - [Features](#features) - [Customization](#customization) - [Number Format API](#number-format-api) - [Options API](#options-api) - [Documentation](#documentation) - [Validation](#validation) - [Metrics](#metrics) - [Safety](#safety) - [Platform Support](#platform-support) - [Versioning and Version Support](#versioning-and-version-support) - [Changelog](#changelog) - [License](#license) - [Contributing](#contributing) ## Getting Started Add lexical to your `Cargo.toml`: ```toml [dependencies] lexical-core = "^1.0" ``` And get started using lexical: ```rust // Number to string use lexical_core::BUFFER_SIZE; let mut buffer = [b'0'; BUFFER_SIZE]; lexical_core::write(3.0, &mut buffer); // "3.0", always has a fraction suffix, lexical_core::write(3, &mut buffer); // "3" // String to number. let i: i32 = lexical_core::parse("3")?; // Ok(3), auto-type deduction. let f: f32 = lexical_core::parse("3.5")?; // Ok(3.5) let d: f64 = lexical_core::parse("3.5")?; // Ok(3.5), error checking parse. let d: f64 = lexical_core::parse("3a")?; // Err(Error(_)), failed to parse. ``` In order to use lexical in generic code, the trait bounds `FromLexical` (for `parse`) and `ToLexical` (for `to_string`) are provided. ```rust /// Multiply a value in a string by multiplier, and serialize to string. fn mul_2(value: &str, multiplier: T) -> Result where T: lexical_core::ToLexical + lexical_core::FromLexical, { let value: T = lexical_core::parse(value.as_bytes())?; let mut buffer = [b'0'; lexical_core::BUFFER_SIZE]; let bytes = lexical_core::write(value * multiplier, &mut buffer); Ok(std::str::from_utf8(bytes).unwrap()) } ``` ## Partial/Complete Parsers Lexical has both partial and complete parsers: the complete parsers ensure the entire buffer is used while parsing, without ignoring trailing characters, while the partial parsers parse as many characters as possible, returning both the parsed value and the number of parsed digits. Upon encountering an error, lexical will return an error indicating both the error type and the index at which the error occurred inside the buffer. **Complete Parsers** ```rust // This will return Err(Error::InvalidDigit(3)), indicating // the first invalid character occurred at the index 3 in the input // string (the space character). let x: i32 = lexical_core::parse(b"123 456")?; ``` **Partial Parsers** ```rust // This will return Ok((123, 3)), indicating that 3 digits were successfully // parsed, and that the returned value is `123`. let (x, count): (i32, usize) = lexical_core::parse_partial(b"123 456")?; ``` ## no_std `lexical-core` does not depend on a standard library, nor a system allocator. To use `lexical-core` in a `no_std` environment, add the following to `Cargo.toml`: ```toml [dependencies.lexical-core] version = "1.0.0" default-features = false # Can select only desired parsing/writing features. features = ["write-integers", "write-floats", "parse-integers", "parse-floats"] ``` And get started using lexical: ```rust // A constant for the maximum number of bytes a formatter will write. use lexical_core::BUFFER_SIZE; let mut buffer = [b'0'; BUFFER_SIZE]; // Number to string. The underlying buffer must be a slice of bytes. let count = lexical_core::write(3.0, &mut buffer); assert_eq!(buffer[..count], b"3.0"); let count = lexical_core::write(3i32, &mut buffer); assert_eq!(buffer[..count], b"3"); // String to number. The input must be a slice of bytes. let i: i32 = lexical_core::parse(b"3")?; // Ok(3), auto-type deduction. let f: f32 = lexical_core::parse(b"3.5")?; // Ok(3.5) let d: f64 = lexical_core::parse(b"3.5")?; // Ok(3.5), error checking parse. let d: f64 = lexical_core::parse(b"3a")?; // Err(Error(_)), failed to parse. ``` ## Features Lexical feature-gates each numeric conversion routine, resulting in faster compile times if certain numeric conversions. These features can be enabled/disabled for both `lexical-core` (which does not require a system allocator) and `lexical`. By default, all conversions are enabled. - **parse-floats**:   Enable string-to-float conversions. - **parse-integers**:   Enable string-to-integer conversions. - **write-floats**:   Enable float-to-string conversions. - **write-integers**:   Enable integer-to-string conversions. Lexical is highly customizable, and contains numerous other optional features: - **std**:   Enable use of the Rust standard library (enabled by default). - **power-of-two**:   Enable conversions to and from non-decimal strings.
With power_of_two enabled, the radixes {2, 4, 8, 10, 16, and 32} are valid, otherwise, only 10 is valid. This enables common conversions to/from hexadecimal integers/floats, without requiring large pre-computed tables for other radixes.
- **radix**:   Allow conversions to and from non-decimal strings.
With radix enabled, any radix from 2 to 36 (inclusive) is valid, otherwise, only 10 is valid.
- **format**:   Customize acceptable number formats for number parsing and writing.
With format enabled, the number format is dictated through bitflags and masks packed into a u128. These dictate the valid syntax of parsed and written numbers, including enabling digit separators, requiring integer or fraction digits, and toggling case-sensitive exponent characters.
- **compact**:   Optimize for binary size at the expense of performance.
This minimizes the use of pre-computed tables, producing significantly smaller binaries.
- **f16**:   Add support for numeric conversions to-and-from 16-bit floats.
Adds f16, a half-precision IEEE-754 floating-point type, and bf16, the Brain Float 16 type, and numeric conversions to-and-from these floats. Note that since these are storage formats, and therefore do not have native arithmetic operations, all conversions are done using an intermediate f32.
To ensure memory safety, we extensively fuzz the all numeric conversion routines. See the [Safety](#safety) section below for more information. Lexical also places a heavy focus on code bloat: with algorithms both optimized for performance and size. By default, this focuses on performance, however, using the `compact` feature, you can also opt-in to reduced code size at the cost of performance. The compact algorithms minimize the use of pre-computed tables and other optimizations at a major cost to performance. ## Customization Lexical is extensively customizable to support parsing numbers from a wide variety of programming languages, such as `1_2_3`. However, lexical takes the concept of "you don't pay for what you don't use" seriously: enabling the `format` feature does not affect the performance of parsing regular numbers: only those with digit separators. > ⚠ **WARNING:** When changing the number of significant digits written, disabling the use of exponent notation, or changing exponent notation thresholds, `BUFFER_SIZE` may be insufficient to hold the resulting output. `WriteOptions::buffer_size` will provide a correct upper bound on the number of bytes written. If a buffer of insufficient length is provided, lexical-core will panic. Every language has competing specifications for valid numerical input, meaning a number parser for Rust will incorrectly accept or reject input for different programming or data languages. For example: ```rust // Valid in Rust strings. // Not valid in JSON. let f: f64 = lexical_core::parse(b"3.e7")?; // 3e7 // Let's only accept JSON floats. const JSON: u128 = lexical_core::format::JSON; let options = ParseFloatOptions::new(); let f: f64 = lexical_core::parse_with_options::(b"3.0e7", &options)?; // 3e7 let f: f64 = lexical_core::parse_with_options::(b"3.e7", &options)?; // Errors! ``` Due the high variability in the syntax of numbers in different programming and data languages, we provide 2 different APIs to simplify converting numbers with different syntax requirements. - Number Format API (feature-gated via `format` or `power-of-two`).
This is a packed struct contained flags to specify compile-time syntax rules for number parsing or writing. This includes features such as the radix of the numeric string, digit separators, case-sensitive exponent characters, optional base prefixes/suffixes, and more.
- Options API.
This contains run-time rules for parsing and writing numbers. This includes exponent break points, rounding modes, the exponent and decimal point characters, and the string representation of NaN and Infinity.
A limited subset of functionality is documented in examples below, however, the complete specification can be found in the API reference documentation ([parse-float](https://docs.rs/lexical-parse-float/latest/lexical_parse_float/struct.Options.html), [parse-integer](https://docs.rs/lexical-parse-integer/latest/lexical_parse_integer/struct.Options.html), and [write-float](https://docs.rs/lexical-write-float/latest/lexical_write_float/struct.Options.html)). ### Number Format API The number format class provides numerous flags to specify number syntax when parsing or writing. When the `power-of-two` feature is enabled, additional flags are added: - The radix for the significant digits (default `10`). - The radix for the exponent base (default `10`). - The radix for the exponent digits (default `10`). When the `format` feature is enabled, numerous other syntax and digit separator flags are enabled, including: - A digit separator character, to group digits for increased legibility. - Whether leading, trailing, internal, and consecutive digit separators are allowed. - Toggling required float components, such as digits before the decimal point. - Toggling whether special floats are allowed or are case-sensitive. Many pre-defined constants therefore exist to simplify common use-cases, including: - JSON, XML, TOML, YAML, SQLite, and many more. - Rust, Python, C#, FORTRAN, COBOL literals and strings, and many more. An example of building a custom number format is as follows: ```rust const FORMAT: u128 = lexical_core::NumberFormatBuilder::new() // Disable exponent notation. .no_exponent_notation(true) // Disable all special numbers, such as Nan and Inf. .no_special(true) .build(); // Due to use in a `const fn`, we can't panic or expect users to unwrap invalid // formats, so it's up to the caller to verify the format. If an invalid format // is provided to a parser or writer, the function will error or panic, respectively. debug_assert!(lexical_core::format_is_valid::()); ``` ### Options API The options API allows customizing number parsing and writing at run-time, such as specifying the maximum number of significant digits, exponent characters, and more. An example of building a custom options struct is as follows: ```rust use std::num; let options = lexical_core::WriteFloatOptions::builder() // Only write up to 5 significant digits, IE, `1.23456` becomes `1.2345`. .max_significant_digits(num::NonZeroUsize::new(5)) // Never write less than 5 significant digits, `1.1` becomes `1.1000`. .min_significant_digits(num::NonZeroUsize::new(5)) // Trim the trailing `.0` from integral float strings. .trim_floats(true) // Use a European-style decimal point. .decimal_point(b',') // Panic if we try to write NaN as a string. .nan_string(None) // Write infinity as "Infinity". .inf_string(Some(b"Infinity")) .build() .unwrap(); ``` ## Documentation Lexical's API reference can be found on [docs.rs](https://docs.rs/lexical), as can [lexical-core's](lexical-core). Detailed descriptions of the algorithms used can be found here: - [Parsing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Algorithm.md) - [Parsing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Algorithm.md) - [Writing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Algorithm.md) - [Writing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Algorithm.md) In addition, descriptions of how lexical handles [digit separators](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/DigitSeparators.md) and implements [big-integer arithmetic](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/BigInteger.md) are also documented. ## Validation **Float-Parsing** Float parsing is difficult to do correctly, and major bugs have been found in implementations from [libstdc++'s strtod](https://www.exploringbinary.com/glibc-strtod-incorrectly-converts-2-to-the-negative-1075/) to [Python](https://bugs.python.org/issue7632). In order to validate the accuracy of the lexical, we employ the following external tests: 1. Hrvoje Abraham's [strtod](https://github.com/ahrvoje/numerics/tree/master/strtod) test cases. 2. Rust's [test-float-parse](https://github.com/rust-lang/rust/tree/64185f205dcbd8db255ad6674e43c63423f2369a/src/etc/test-float-parse) unittests. 3. Testbase's [stress tests](https://www.icir.org/vern/papers/testbase-report.pdf) for converting from decimal to binary. 4. Nigel Tao's [tests](https://github.com/nigeltao/parse-number-fxx-test-data) extracted from test suites for Freetype, Google's double-conversion library, IBM's IEEE-754R compliance test, as well as numerous other curated examples. 5. [Various](https://www.exploringbinary.com/glibc-strtod-incorrectly-converts-2-to-the-negative-1075/) [difficult](https://www.exploringbinary.com/how-glibc-strtod-works/) [cases](https://www.exploringbinary.com/how-strtod-works-and-sometimes-doesnt/) reported on blogs. Lexical is extensively used in production, the same float parsing algorithm has been adopted by Golang's and Rust's standard libraries, and is unlikely to have correctness issues. ## Metrics Various benchmarks, binary sizes, and compile times are shown here. All the benchmarks can be found on [lexical-benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks?tab=readme-ov-file#latest-results). All benchmarks used a black box to avoid optimizing out the result and leading to misleading metrics. **Build Timings** The compile-times when building with all numeric conversions enabled. For a more fine-tuned breakdown, see [build timings](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BuildTimings.md). ![Build Timings](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/timings_all_posix.svg) **Binary Size** The binary sizes of stripped binaries compiled at optimization level "2". For a more fine-tuned breakdown, see [binary sizes](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BinarySize.md). ![Parse Stripped - Optimization Level "2"](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/size_parse_stripped_opt2_posix.svg) ![Write Stripped - Optimization Level "2"](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/size_write_stripped_opt2_posix.svg) ### Benchmarks — Parse Integer **Random** A benchmark on randomly-generated integers uniformly distributed over the entire range. ![Uniform Random Data](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/json_random%20-%20parse%20int%20-%20core,lexical.png) **Simple** A benchmark on randomly-generated integers from 1-1000. ![Simple Random Data](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/json_simple%20-%20parse%20int%20-%20core,lexical.png) ### Benchmarks — Parse Float **Real-World Datasets** A benchmark on parsing floats from various real-world data sets, including Canada, Mesh, and astronomical data (earth). ![Canada](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/canada%20-%20parse%20float%20-%20core,lexical.png) ![Earth](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/earth%20-%20parse%20float%20-%20core,lexical.png) ![Mesh](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/mesh%20-%20parse%20float%20-%20core,lexical.png) **Random** A benchmark on randomly-generated integers uniformly distributed over the entire range. ![Random Big Integer](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/random_big_ints%20-%20parse%20float%20-%20core,lexical.png) **Simple** A benchmark on randomly-generated integers from 1-1000. ![Random Simple](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/random_simple_int64%20-%20parse%20float%20-%20core,lexical.png) ### Benchmarks — Write Integer **Random** A benchmark on randomly-generated integers uniformly distributed over the entire range. ![Random Uniform](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/json_chain_random%20-%20write%20int%20-%20fmt,itoa,lexical.png) **Simple** ![Random Simple](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/json_simple%20-%20write%20int%20-%20fmt,itoa,lexical.png) **Large** ![Random Large](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/random_large%20-%20write%20int%20-%20fmt,itoa,lexical.png) ### Benchmarks — Write Float **Big Integer** A benchmarks for values with a large integers. ![Big Integers](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/random_big_ints%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png) **Simple 64-Bit Inteers** ![Simple Int64](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/random_simple_int64%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png) **Random** ![Random](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/json%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png) ## Safety Due to the use of memory unsafe code in the library, we extensively fuzz our float writers and parsers. The fuzz harnesses may be found under [fuzz](https://github.com/Alexhuszagh/rust-lexical/tree/main/fuzz), and are run continuously. So far, we've parsed and written over 72 billion floats. ## Platform Support lexical-core is tested on a wide variety of platforms, including big and small-endian systems, to ensure portable code. Supported architectures include: - x86_64 Linux, Windows, macOS, Android, iOS, FreeBSD, and NetBSD. - x86 Linux, macOS, Android, iOS, and FreeBSD. - aarch64 (ARM8v8-A) Linux, Android, and iOS. - armv7 (ARMv7-A) Linux, Android, and iOS. - arm (ARMv6) Linux, and Android. - powerpc (PowerPC) Linux. - powerpc64 (PPC64) Linux. - powerpc64le (PPC64LE) Linux. - s390x (IBM Z) Linux. lexical-core should also work on a wide variety of other architectures and ISAs. If you have any issue compiling lexical-core on any architecture, please file a bug report. ## Versioning and Version Support **Version Support** The currently supported versions are: - v1.0.x Due to security considerations, all other versions are not supported and security advisories exist for them.. **Rustc Compatibility** - v1.0.x supports 1.63+, including stable, beta, and nightly. Please report any errors compiling a supported lexical-core version on a compatible Rustc version. **Versioning** lexical uses [semantic versioning](https://semver.org/). Removing support for Rustc versions newer than the latest stable Debian or Ubuntu version is considered an incompatible API change, requiring a major version change. ## Changelog All changes are documented in [CHANGELOG](https://github.com/Alexhuszagh/rust-lexical/blob/main/CHANGELOG). ## License Lexical is dual licensed under the Apache 2.0 license as well as the MIT license. See the [LICENSE.md](LICENSE.md) file for full license details. ## Contributing Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in lexical by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. Contributing to the repository means abiding by the [code of conduct](https://github.com/Alexhuszagh/rust-lexical/blob/main/CODE_OF_CONDUCT.md). For the process on how to contribute to lexical, see the [development](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/Development.md) quick-start guide. lexical-parse-integer-1.0.5/src/algorithm.rs000064400000000000000000000710301046102023000171370ustar 00000000000000//! Radix-generic, optimized, string-to-integer conversion routines. //! //! These routines are highly optimized: they use various optimizations //! to read multiple digits at-a-time with less multiplication instructions, //! as well as other optimizations to avoid unnecessary compile-time branching. //! //! See [Algorithm.md](/docs/Algorithm.md) for a more detailed description of //! the algorithm choice here. See [Benchmarks.md](/docs/Benchmarks.md) for //! recent benchmark data. //! //! These allow implementations of partial and complete parsers //! using a single code-path via macros. //! //! This looks relatively, complex, but it's quite simple. Almost all //! of these branches are resolved at compile-time, so the resulting //! code is quite small while handling all of the internal complexity. //! //! 1. Helpers to process ok and error results for both complete and partial //! parsers. They have different APIs, and mixing the APIs leads to //! substantial performance hits. //! 2. Overflow checking on invalid digits for partial parsers, while just //! returning invalid digits for complete parsers. //! 3. A format-aware sign parser. //! 4. Digit parsing algorithms which explicitly wrap on overflow, for no //! additional overhead. This has major performance wins for **most** //! real-world integers, so most valid input will be substantially faster. //! 5. An algorithm to detect if overflow occurred. This is comprehensive, and //! short-circuits for common cases. //! 6. A parsing algorithm for unsigned integers, always producing positive //! values. This avoids any unnecessary branching. //! 7. Multi-digit optimizations for larger sizes. #![doc(hidden)] use lexical_util::digit::char_to_digit_const; use lexical_util::error::Error; use lexical_util::format::NumberFormat; use lexical_util::iterator::{AsBytes, Bytes, DigitsIter, Iter}; use lexical_util::num::{as_cast, Integer}; use lexical_util::result::Result; use crate::Options; // HELPERS /// Check if we should do multi-digit optimizations const fn can_try_parse_multidigits<'a, Iter: DigitsIter<'a>, const FORMAT: u128>(_: &Iter) -> bool { let format = NumberFormat:: {}; Iter::IS_CONTIGUOUS && (cfg!(not(feature = "power-of-two")) || format.mantissa_radix() <= 10) } // Get if digits are required for the format. #[cfg_attr(not(feature = "format"), allow(unused_macros))] macro_rules! required_digits { () => { NumberFormat::::REQUIRED_INTEGER_DIGITS || NumberFormat::::REQUIRED_MANTISSA_DIGITS }; } /// Return an value for a complete parser. macro_rules! into_ok_complete { ($value:expr, $index:expr, $count:expr) => {{ #[cfg(not(feature = "format"))] return Ok(as_cast($value)); #[cfg(feature = "format")] if required_digits!() && $count == 0 { into_error!(Empty, $index); } else { return Ok(as_cast($value)); } }}; } /// Return an value and index for a partial parser. macro_rules! into_ok_partial { ($value:expr, $index:expr, $count:expr) => {{ #[cfg(not(feature = "format"))] return Ok((as_cast($value), $index)); #[cfg(feature = "format")] if required_digits!() && $count == 0 { into_error!(Empty, $index); } else { return Ok((as_cast($value), $index)); } }}; } /// Return an error for a complete parser upon an invalid digit. macro_rules! invalid_digit_complete { ($value:expr, $index:expr, $count:expr) => { // Don't do any overflow checking here: we don't need it. into_error!(InvalidDigit, $index - 1) }; } /// Return a value for a partial parser upon an invalid digit. /// This checks for numeric overflow, and returns the appropriate error. macro_rules! invalid_digit_partial { ($value:expr, $index:expr, $count:expr) => { // NOTE: The value is already positive/negative into_ok_partial!($value, $index - 1, $count) }; } /// Return an error, returning the index and the error. macro_rules! into_error { ($code:ident, $index:expr) => {{ return Err(Error::$code($index)); }}; } /// Handle an invalid digit if the format feature is enabled. /// /// This is because we can have special, non-digit characters near /// the start or internally. If `$is_end` is set to false, there **MUST** /// be elements in the underlying slice after the current iterator. #[cfg(feature = "format")] macro_rules! fmt_invalid_digit { ( $value:ident, $iter:ident, $c:expr, $start_index:ident, $invalid_digit:ident, $is_end:expr ) => {{ // NOTE: If we have non-contiguous iterators, we could have a skip character // here at the boundary. This does not affect safety but it does affect // correctness. debug_assert!($iter.is_contiguous() || $is_end); let base_suffix = NumberFormat::::BASE_SUFFIX; let uncased_base_suffix = NumberFormat::::CASE_SENSITIVE_BASE_SUFFIX; // Need to check for a base suffix, if so, return a valid value. // We can't have a base suffix at the first value (need at least // 1 digit). if base_suffix != 0 && $iter.cursor() - $start_index > 1 { let is_suffix = if uncased_base_suffix { $c == base_suffix } else { $c.eq_ignore_ascii_case(&base_suffix) }; // NOTE: If we're using the `take_n` optimization where it can't // be the end, then the iterator cannot be done. So, in that case, // we need to end. `take_n` also can never be used for non- // contiguous iterators. if is_suffix && $is_end && $iter.is_buffer_empty() { // Break out of the loop, we've finished parsing. break; } else if !$iter.is_buffer_empty() { // Haven't finished parsing, so we're going to call // `invalid_digit!`. Need to ensure we include the // base suffix in that. // SAFETY: safe since the iterator is not empty, as checked // in `$iter.is_buffer_empty()`. Adding in the check hopefully // will be elided since it's a known constant. unsafe { $iter.step_unchecked() }; } } // Might have handled our base-prefix here. $invalid_digit!($value, $iter.cursor(), $iter.current_count()) }}; } /// Just return an invalid digit #[cfg(not(feature = "format"))] macro_rules! fmt_invalid_digit { ( $value:ident, $iter:ident, $c:expr, $start_index:ident, $invalid_digit:ident, $is_end:expr ) => {{ $invalid_digit!($value, $iter.cursor(), $iter.current_count()); }}; } /// Parse the sign from the leading digits. /// /// This routine does the following: /// /// 1. Parses the sign digit. /// 2. Handles if positive signs before integers are not allowed. /// 3. Handles negative signs if the type is unsigned. /// 4. Handles if the sign is required, but missing. /// 5. Handles if the iterator is empty, before or after parsing the sign. /// 6. Handles if the iterator has invalid, leading zeros. /// /// Returns if the value is negative, or any values detected when /// validating the input. #[macro_export] macro_rules! parse_sign { ( $byte:ident, $is_signed:expr, $no_positive:expr, $required:expr, $invalid_positive:ident, $missing:ident ) => { // NOTE: `read_if` optimizes poorly since we then match after match $byte.integer_iter().first() { Some(&b'+') if !$no_positive => { // SAFETY: We have at least 1 item left since we peaked a value unsafe { $byte.step_unchecked() }; Ok(false) }, Some(&b'+') if $no_positive => Err(Error::$invalid_positive($byte.cursor())), Some(&b'-') if $is_signed => { // SAFETY: We have at least 1 item left since we peaked a value unsafe { $byte.step_unchecked() }; Ok(true) }, Some(_) if $required => Err(Error::$missing($byte.cursor())), _ if $required => Err(Error::$missing($byte.cursor())), _ => Ok(false), } }; } /// Parse the sign from the leading digits. #[cfg_attr(not(feature = "compact"), inline(always))] pub fn parse_sign(byte: &mut Bytes<'_, FORMAT>) -> Result { let format = NumberFormat:: {}; parse_sign!( byte, T::IS_SIGNED, format.no_positive_mantissa_sign(), format.required_mantissa_sign(), InvalidPositiveSign, MissingSign ) } // FOUR DIGITS /// Determine if 4 bytes, read raw from bytes, are 4 digits for the radix. #[cfg_attr(not(feature = "compact"), inline(always))] pub fn is_4digits(v: u32) -> bool { let radix = NumberFormat::<{ FORMAT }>::MANTISSA_RADIX; debug_assert!(radix <= 10); // We want to have a wrapping add and sub such that only values from the // range `[0x30, 0x39]` (or narrower for custom radixes) will not // overflow into the high bit. This means that the value needs to overflow // into into `0x80` if the digit is 1 above, or `0x46` for the value `0x39`. // Likewise, we only valid for `[0x30, 0x38]` for radix 8, so we need // `0x47`. let add = 0x46 + 10 - radix; let add = add + (add << 8) + (add << 16) + (add << 24); // This aims to underflow if anything is below the min digit: if we have any // values under `0x30`, then this underflows and wraps into the high bit. let sub = 0x3030_3030; let a = v.wrapping_add(add); let b = v.wrapping_sub(sub); (a | b) & 0x8080_8080 == 0 } /// Parse 4 bytes read from bytes into 4 digits. #[cfg_attr(not(feature = "compact"), inline(always))] pub fn parse_4digits(mut v: u32) -> u32 { let radix = NumberFormat::<{ FORMAT }>::MANTISSA_RADIX; debug_assert!(radix <= 10); // Normalize our digits to the range `[0, 9]`. v -= 0x3030_3030; // Scale digits in `0 <= Nn <= 99`. v = (v * radix) + (v >> 8); // Scale digits in `0 <= Nnnn <= 9999`. v = ((v & 0x0000007f) * radix * radix) + ((v >> 16) & 0x0000007f); v } /// Use a fast-path optimization, where we attempt to parse 4 digits at a time. /// This reduces the number of multiplications necessary to 2, instead of 4. /// /// This approach is described in full here: /// #[cfg_attr(not(feature = "compact"), inline(always))] pub fn try_parse_4digits<'a, T, Iter, const FORMAT: u128>(iter: &mut Iter) -> Option where T: Integer, Iter: DigitsIter<'a>, { // Can't do fast optimizations with radixes larger than 10, since // we no longer have a contiguous ASCII block. Likewise, cannot // use non-contiguous iterators. debug_assert!(NumberFormat::<{ FORMAT }>::MANTISSA_RADIX <= 10); debug_assert!(Iter::IS_CONTIGUOUS); // Read our digits, validate the input, and check from there. let bytes = u32::from_le(iter.peek_u32()?); if is_4digits::(bytes) { // SAFETY: safe since we have at least 4 bytes in the buffer. unsafe { iter.step_by_unchecked(4) }; Some(T::as_cast(parse_4digits::(bytes))) } else { None } } // EIGHT DIGITS /// Determine if 8 bytes, read raw from bytes, are 8 digits for the radix. /// See `is_4digits` for the algorithm description. #[cfg_attr(not(feature = "compact"), inline(always))] pub fn is_8digits(v: u64) -> bool { let radix = NumberFormat::<{ FORMAT }>::MANTISSA_RADIX; debug_assert!(radix <= 10); let add = 0x46 + 10 - radix; let add = add + (add << 8) + (add << 16) + (add << 24); let add = (add as u64) | ((add as u64) << 32); // This aims to underflow if anything is below the min digit: if we have any // values under `0x30`, then this underflows and wraps into the high bit. let sub = 0x3030_3030_3030_3030; let a = v.wrapping_add(add); let b = v.wrapping_sub(sub); (a | b) & 0x8080_8080_8080_8080 == 0 } /// Parse 8 bytes read from bytes into 8 digits. /// Credit for this goes to @aqrit, which further optimizes the /// optimization described by Johnny Lee above. #[cfg_attr(not(feature = "compact"), inline(always))] pub fn parse_8digits(mut v: u64) -> u64 { let radix = NumberFormat::<{ FORMAT }>::MANTISSA_RADIX as u64; debug_assert!(radix <= 10); // Create our masks. Assume the optimizer will do this at compile time. // It seems like an optimizing compiler **will** do this, so we // should be safe. let radix2 = radix * radix; let radix4 = radix2 * radix2; let radix6 = radix2 * radix4; let mask = 0x0000_00FF_0000_00FFu64; let mul1 = radix2 + (radix6 << 32); let mul2 = 1 + (radix4 << 32); // Normalize our digits to the base. v -= 0x3030_3030_3030_3030; // Scale digits in `0 <= Nn <= 99`. v = (v * radix) + (v >> 8); let v1 = (v & mask).wrapping_mul(mul1); let v2 = ((v >> 16) & mask).wrapping_mul(mul2); ((v1.wrapping_add(v2) >> 32) as u32) as u64 } /// Use a fast-path optimization, where we attempt to parse 8 digits at a time. /// This reduces the number of multiplications necessary to 3, instead of 8. #[cfg_attr(not(feature = "compact"), inline(always))] pub fn try_parse_8digits<'a, T, Iter, const FORMAT: u128>(iter: &mut Iter) -> Option where T: Integer, Iter: DigitsIter<'a>, { // Can't do fast optimizations with radixes larger than 10, since // we no longer have a contiguous ASCII block. Likewise, cannot // use non-contiguous iterators. debug_assert!(NumberFormat::<{ FORMAT }>::MANTISSA_RADIX <= 10); debug_assert!(Iter::IS_CONTIGUOUS); // Read our digits, validate the input, and check from there. let bytes = u64::from_le(iter.peek_u64()?); if is_8digits::(bytes) { // SAFETY: safe since we have at least 8 bytes in the buffer. unsafe { iter.step_by_unchecked(8) }; Some(T::as_cast(parse_8digits::(bytes))) } else { None } } // ONE DIGIT /// Run a loop where the integer cannot possibly overflow. /// /// If the length of the str is short compared to the range of the type /// we are parsing into, then we can be certain that an overflow will not occur. /// This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition /// above is a faster (conservative) approximation of this. /// /// Consider radix 16 as it has the highest information density per digit and /// will thus overflow the earliest: `u8::MAX` is `ff` - any str of length 2 is /// guaranteed to not overflow. `i8::MAX` is `7f` - only a str of length 1 is /// guaranteed to not overflow. /// /// This is based off of [core/num](core). /// /// * `value` - The current parsed value. /// * `iter` - An iterator over all bytes in the input. /// * `add_op` - The unchecked add/sub op. /// * `start_index` - The offset where parsing started. /// * `invalid_digit` - Behavior when an invalid digit is found. /// * `is_end` - If iter corresponds to the full input. /// /// core: macro_rules! parse_1digit_unchecked { ( $value:ident, $iter:ident, $add_op:ident, $start_index:ident, $invalid_digit:ident, $is_end:expr ) => {{ // This is a slower parsing algorithm, going 1 digit at a time, but doing it in // an unchecked loop. let radix = NumberFormat::::MANTISSA_RADIX; while let Some(&c) = $iter.next() { let digit = match char_to_digit_const(c, radix) { Some(v) => v, None => fmt_invalid_digit!($value, $iter, c, $start_index, $invalid_digit, $is_end), }; // multiply first since compilers are good at optimizing things out and will do // a fused mul/add We must do this after getting the digit for // partial parsers $value = $value.wrapping_mul(as_cast(radix)).$add_op(as_cast(digit)); } }}; } /// Run a loop where the integer could overflow. /// /// This is a standard, unoptimized algorithm. This is based off of /// [core/num](core) /// /// * `value` - The current parsed value. /// * `iter` - An iterator over all bytes in the input. /// * `add_op` - The checked add/sub op. /// * `start_index` - The offset where parsing started. /// * `invalid_digit` - Behavior when an invalid digit is found. /// * `overflow` - If the error is overflow or underflow. /// /// core: macro_rules! parse_1digit_checked { ( $value:ident, $iter:ident, $add_op:ident, $start_index:ident, $invalid_digit:ident, $overflow:ident ) => {{ // This is a slower parsing algorithm, going 1 digit at a time, but doing it in // an unchecked loop. let radix = NumberFormat::::MANTISSA_RADIX; while let Some(&c) = $iter.next() { let digit = match char_to_digit_const(c, radix) { Some(v) => v, None => fmt_invalid_digit!($value, $iter, c, $start_index, $invalid_digit, true), }; // multiply first since compilers are good at optimizing things out and will do // a fused mul/add $value = match $value.checked_mul(as_cast(radix)).and_then(|x| x.$add_op(as_cast(digit))) { Some(value) => value, None => into_error!($overflow, $iter.cursor() - 1), } } }}; } // OVERALL DIGITS // -------------- /// Run an unchecked loop where digits are processed incrementally. /// /// If the type size is small or there's not many digits, skip multi-digit /// optimizations. Otherwise, if the type size is large and we're not manually /// skipping manual optimizations, then we do this here. /// /// * `value` - The current parsed value. /// * `iter` - An iterator over all bytes in the input. /// * `add_op` - The unchecked add/sub op. /// * `start_index` - The offset where parsing started. /// * `invalid_digit` - Behavior when an invalid digit is found. /// * `no_multi_digit` - If to disable multi-digit optimizations. /// * `is_end` - If iter corresponds to the full input. macro_rules! parse_digits_unchecked { ( $value:ident, $iter:ident, $add_op:ident, $start_index:ident, $invalid_digit:ident, $no_multi_digit:expr, $is_end:expr ) => {{ let can_multi = can_try_parse_multidigits::<_, FORMAT>(&$iter); let use_multi = can_multi && !$no_multi_digit; // these cannot overflow. also, we use at most 3 for a 128-bit float and 1 for a // 64-bit float NOTE: Miri will complain about this if we use radices >= // 16 but since they won't go into `try_parse_8digits!` or // `try_parse_4digits` it will be optimized out and the overflow won't // matter. let format = NumberFormat:: {}; if use_multi && T::BITS >= 64 && $iter.buffer_length() >= 8 { // Try our fast, 8-digit at a time optimizations. let radix8 = T::from_u32(format.radix8()); while let Some(value) = try_parse_8digits::(&mut $iter) { $value = $value.wrapping_mul(radix8).$add_op(value); } } else if use_multi && T::BITS == 32 && $iter.buffer_length() >= 4 { // Try our fast, 8-digit at a time optimizations. let radix4 = T::from_u32(format.radix4()); while let Some(value) = try_parse_4digits::(&mut $iter) { $value = $value.wrapping_mul(radix4).$add_op(value); } } parse_1digit_unchecked!($value, $iter, $add_op, $start_index, $invalid_digit, $is_end) }}; } /// Run checked loop where digits are processed with overflow checking. /// /// If the type size is small or there's not many digits, skip multi-digit /// optimizations. Otherwise, if the type size is large and we're not manually /// skipping manual optimizations, then we do this here. /// /// * `value` - The current parsed value. /// * `iter` - An iterator over all bytes in the input. /// * `add_op` - The checked add/sub op. /// * `add_op_uc` - The unchecked add/sub op for small digit optimizations. /// * `start_index` - The offset where parsing started. /// * `invalid_digit` - Behavior when an invalid digit is found. /// * `overflow` - If the error is overflow or underflow. /// * `no_multi_digit` - If to disable multi-digit optimizations. /// * `overflow_digits` - The number of digits before we need to consider /// checked ops. macro_rules! parse_digits_checked { ( $value:ident, $iter:ident, $add_op:ident, $add_op_uc:ident, $start_index:ident, $invalid_digit:ident, $overflow:ident, $no_multi_digit:expr, $overflow_digits:expr ) => {{ // Can use the unchecked for the `max_digits` here. If we // have a non-contiguous iterator, we could have a case like // 123__456, with no consecutive digit separators allowed. If // it's broken between the `_` characters, the integer will be // seen as valid when it isn't. if cfg!(not(feature = "format")) || $iter.is_contiguous() { if let Some(mut small) = $iter.take_n($overflow_digits) { let mut small_iter = small.integer_iter(); parse_digits_unchecked!( $value, small_iter, $add_op_uc, $start_index, $invalid_digit, $no_multi_digit, false ); } } // NOTE: all our multi-digit optimizations have been done here: skip this parse_1digit_checked!($value, $iter, $add_op, $start_index, $invalid_digit, $overflow) }}; } // ALGORITHM /// Generic algorithm for both partial and complete parsers. /// /// * `invalid_digit` - Behavior on finding an invalid digit. /// * `into_ok` - Behavior when returning a valid value. /// * `invalid_digit` - Behavior when an invalid digit is found. /// * `no_multi_digit` - If to disable multi-digit optimizations. /// * `is_partial` - If the parser is a partial parser. #[rustfmt::skip] macro_rules! algorithm { ($bytes:ident, $into_ok:ident, $invalid_digit:ident, $no_multi_digit:expr) => {{ // WARNING: // -------- // None of this code can be changed for optimization reasons. // Do not change it without benchmarking every change. // 1. You cannot use the `NoSkipIterator` in the loop, // you must either return a subslice (indexing) // or increment outside of the loop. // Failing to do so leads to numerous more, unnecessary // conditional move instructions, killing performance. // 2. Return a 0 or 1 shift, and indexing unchecked outside // of the loop is slightly faster. // 3. Partial and complete parsers cannot be efficiently done // together. // // If you try to refactor without carefully monitoring benchmarks or // assembly generation, please log the number of wasted hours: so // 16 hours so far. // With `step_by_unchecked`, this is sufficiently optimized. // Removes conditional paths, to, which simplifies maintenance. // The skip version of the iterator automatically coalesces to // the no-skip iterator. let mut byte = $bytes.bytes::(); let radix = NumberFormat::::MANTISSA_RADIX; let is_negative = parse_sign::(&mut byte)?; let mut iter = byte.integer_iter(); if iter.is_buffer_empty() { // Our default format **ALWAYS** requires significant digits, however, // we can have cases where we don #[cfg(not(feature = "format"))] into_error!(Empty, iter.cursor()); #[cfg(feature = "format")] if required_digits!() { into_error!(Empty, iter.cursor()); } else { $into_ok!(T::ZERO, iter.cursor(), 0) } } // Feature-gate a lot of format-only code here to simplify analysis with our branching // We only want to skip the zeros if have either require a base prefix or we don't // allow integer leading zeros, since the skip is expensive #[allow(unused_variables, unused_mut)] let mut start_index = iter.cursor(); #[cfg_attr(not(feature = "format"), allow(unused_variables))] let format = NumberFormat:: {}; #[cfg(feature = "format")] if format.has_base_prefix() || format.no_integer_leading_zeros() { // Skip any leading zeros. We want to do our check if it can't possibly overflow after. // For skipping digit-based formats, this approximation is a way over estimate. // NOTE: Skipping zeros is **EXPENSIVE* so we skip that without our format feature let zeros = iter.skip_zeros(); start_index += zeros; // Now, check to see if we have a valid base prefix. let mut is_prefix = false; let base_prefix = format.base_prefix(); if base_prefix != 0 && zeros == 1 { // Check to see if the next character is the base prefix. // We must have a format like `0x`, `0d`, `0o`. Note: if iter.read_if_value(base_prefix, format.case_sensitive_base_prefix()).is_some() { is_prefix = true; if iter.is_buffer_empty() { into_error!(Empty, iter.cursor()); } else { start_index += 1; } } } // If we have a format that doesn't accept leading zeros, // check if the next value is invalid. It's invalid if the // first is 0, and the next is not a valid digit. if !is_prefix && format.no_integer_leading_zeros() && zeros != 0 { // Cannot have a base prefix and no leading zeros. let index = iter.cursor() - zeros; if zeros > 1 { into_error!(InvalidLeadingZeros, index); } // NOTE: Zeros has to be 0 here, so our index == 1 or 2 (depending on sign) match iter.peek().map(|&c| char_to_digit_const(c, format.radix())) { // Valid digit, we have an invalid value. Some(Some(_)) => into_error!(InvalidLeadingZeros, index), // Have a non-digit character that follows. Some(None) => $invalid_digit!(::ZERO, iter.cursor() + 1, iter.current_count()), // No digits following, has to be ok None => $into_ok!(::ZERO, index, iter.current_count()), }; } } // shorter strings cannot possibly overflow so a great optimization let overflow_digits = T::overflow_digits(radix); let cannot_overflow = iter.as_slice().len() <= overflow_digits; // NOTE: // Don't add optimizations for 128-bit integers. // 128-bit multiplication is rather efficient, it's only division // that's very slow. Any shortcut optimizations increasing branching, // and even if parsing a 64-bit integer is marginally faster, it // culminates in **way** slower performance overall for simple // integers, and no improvement for large integers. let mut value = T::ZERO; if cannot_overflow && is_negative { parse_digits_unchecked!(value, iter, wrapping_sub, start_index, $invalid_digit, $no_multi_digit, true); } if cannot_overflow { parse_digits_unchecked!(value, iter, wrapping_add, start_index, $invalid_digit, $no_multi_digit, true); } else if is_negative { parse_digits_checked!(value, iter, checked_sub, wrapping_sub, start_index, $invalid_digit, Underflow, $no_multi_digit, overflow_digits); } else { parse_digits_checked!(value, iter, checked_add, wrapping_add, start_index, $invalid_digit, Overflow, $no_multi_digit, overflow_digits); } $into_ok!(value, iter.buffer_length(), iter.current_count()) }}; } /// Algorithm for the complete parser. #[cfg_attr(not(feature = "compact"), inline(always))] pub fn algorithm_complete(bytes: &[u8], options: &Options) -> Result where T: Integer, { algorithm!(bytes, into_ok_complete, invalid_digit_complete, options.get_no_multi_digit()) } /// Algorithm for the partial parser. #[cfg_attr(not(feature = "compact"), inline(always))] pub fn algorithm_partial( bytes: &[u8], options: &Options, ) -> Result<(T, usize)> where T: Integer, { algorithm!(bytes, into_ok_partial, invalid_digit_partial, options.get_no_multi_digit()) } lexical-parse-integer-1.0.5/src/api.rs000064400000000000000000000050301046102023000157170ustar 00000000000000////! Implements the algorithm in terms of the lexical API. #![doc(hidden)] use lexical_util::format::{NumberFormat, STANDARD}; use lexical_util::{from_lexical, from_lexical_with_options}; use crate::options::{Options, STANDARD as DEFAULT_OPTIONS}; use crate::parse::ParseInteger; /// Implement `FromLexical` for numeric type. /// /// Need to inline these, otherwise code generation is sub-optimal. /// For some reason, it can't determine some of the const evaluations /// can actually be evaluated at compile-time, which causes major branching /// issues. macro_rules! integer_from_lexical { ($($t:ident $unsigned:ident ; )*) => ($( impl FromLexical for $t { #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical(bytes: &[u8]) -> lexical_util::result::Result { Self::parse_complete::(bytes, &DEFAULT_OPTIONS) } #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical_partial( bytes: &[u8], ) -> lexical_util::result::Result<(Self, usize)> { Self::parse_partial::(bytes, &DEFAULT_OPTIONS) } } impl FromLexicalWithOptions for $t { type Options = Options; #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical_with_options( bytes: &[u8], options: &Self::Options, ) -> lexical_util::result::Result { let format = NumberFormat::<{ FORMAT }> {}; if !format.is_valid() { return Err(format.error()); } Self::parse_complete::(bytes, options) } #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical_partial_with_options( bytes: &[u8], options: &Self::Options, ) -> lexical_util::result::Result<(Self, usize)> { let format = NumberFormat::<{ FORMAT }> {}; if !format.is_valid() { return Err(format.error()); } Self::parse_partial::(bytes, options) } } )*) } from_lexical! {} from_lexical_with_options! {} integer_from_lexical! { u8 u8 ; u16 u16 ; u32 u32 ; u64 u64 ; u128 u128 ; usize usize ; i8 u8 ; i16 u16 ; i32 u32 ; i64 u64 ; i128 u128 ; isize usize ; } lexical-parse-integer-1.0.5/src/lib.rs000064400000000000000000000105641046102023000157240ustar 00000000000000//! Fast lexical string-to-integer conversion routines. //! //! The default implementations are highly optimized both for simple //! strings, as well as input with large numbers of digits. In order to //! keep performance optimal for simple strings, we avoid overly branching //! to minimize the number of branches (and therefore optimization checks). //! Most of the branches in the code are resolved at compile-time, and //! the resulting ASM is monitored to ensure there are no regressions. For //! larger strings, a limited number of optimization checks are included //! to try faster, multi-digit parsing algorithms. For 32-bit integers, //! we try to parse 4 digits at a time, and for 64-bit and larger integers, //! we try to parse 8 digits at a time. Attempting both checks leads to //! significant performance penalties for simple strings, so only 1 //! optimization is used at at a time. //! //! In addition, a compact, fallback algorithm uses a naive, simple //! algorithm, parsing only a single digit at a time. This avoid any //! unnecessary branching and produces smaller binaries, but comes //! at a significant performance penalty for integers with more digits. //! //! # Features //! //! * `std` - Use the standard library. //! * `power-of-two` - Add support for parsing power-of-two integer strings. //! * `radix` - Add support for strings of any radix. //! * `format` - Add support for parsing custom integer formats. //! * `compact` - Reduce code size at the cost of performance. //! * `safe` - Ensure only memory-safe indexing is used. //! //! `safe` is a no-op, since all parsers are memory-safe by default. //! //! # Note //! //! Only documented functionality is considered part of the public API: //! any of the modules, internal functions, or structs may change //! release-to-release without major or minor version changes. Use //! internal implementation details at your own risk. //! //! lexical-parse-integer mainly exists as an implementation detail for //! lexical-core, although its API is stable. If you would like to use //! a high-level API that writes to and parses from `String` and `&str`, //! respectively, please look at [lexical](https://crates.io/crates/lexical) //! instead. If you would like an API that supports multiple numeric //! conversions, please look at [lexical-core](https://crates.io/crates/lexical-core) //! instead. //! //! # Version Support //! //! The minimum, standard, required version is 1.63.0, for const generic //! support. Older versions of lexical support older Rust versions. //! //! # Design //! //! - [Algorithm Approach](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Algorithm.md) //! - [Benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Benchmarks.md) //! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks) // FIXME: Implement clippy/allow reasons once we drop support for 1.80.0 and below // Clippy reasons were stabilized in 1.81.0. // We want to have the same safety guarantees as Rust core, // so we allow unused unsafe to clearly document safety guarantees. #![allow(unused_unsafe)] #![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))] #![cfg_attr(not(feature = "std"), no_std)] #![deny( clippy::doc_markdown, clippy::unnecessary_safety_comment, clippy::semicolon_if_nothing_returned, clippy::unwrap_used, clippy::as_underscore, clippy::doc_markdown )] #![allow( // used when concepts are logically separate clippy::match_same_arms, // loss of precision is intentional clippy::integer_division, // mathematical names use 1-character identifiers clippy::min_ident_chars, // these are not cryptographically secure contexts clippy::integer_division_remainder_used, // this can be intentional clippy::module_name_repetitions, // this is intentional: already passing a pointer and need performance clippy::needless_pass_by_value, // we use this for inline formatting for unsafe blocks clippy::semicolon_inside_block, )] pub mod algorithm; pub mod options; pub mod parse; mod api; // Re-exports pub use lexical_util::error::Error; pub use lexical_util::format::{self, NumberFormatBuilder}; pub use lexical_util::options::ParseOptions; pub use lexical_util::result::Result; pub use self::api::{FromLexical, FromLexicalWithOptions}; #[doc(inline)] pub use self::options::{Options, OptionsBuilder}; lexical-parse-integer-1.0.5/src/options.rs000064400000000000000000000110221046102023000166370ustar 00000000000000//! Configuration options for parsing integers. use lexical_util::options::ParseOptions; use lexical_util::result::Result; use static_assertions::const_assert; /// Builder for `Options`. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct OptionsBuilder { /// Disable multi-digit optimizations. /// /// Using multi-digit optimizations allows parsing many digits /// from longer input strings at once which can dramatically /// improve performance (>70%) for long strings, but the /// increased branching can decrease performance for simple /// strings by 5-20%. Choose based on your inputs. no_multi_digit: bool, } impl OptionsBuilder { /// Create new options builder with default options. #[inline(always)] pub const fn new() -> Self { Self { no_multi_digit: true, } } // GETTERS /// Get if we disable the use of multi-digit optimizations. #[inline(always)] pub const fn get_no_multi_digit(&self) -> bool { self.no_multi_digit } // SETTERS /// Set if we disable the use of multi-digit optimizations. #[inline(always)] pub const fn no_multi_digit(mut self, no_multi_digit: bool) -> Self { self.no_multi_digit = no_multi_digit; self } // BUILDERS /// Check if the builder state is valid. #[inline(always)] pub const fn is_valid(&self) -> bool { true } /// Build the Options struct with bounds validation. #[inline(always)] pub const fn build_unchecked(&self) -> Options { Options { no_multi_digit: self.no_multi_digit, } } /// Build the Options struct. #[inline(always)] pub const fn build(&self) -> Result { Ok(self.build_unchecked()) } } impl Default for OptionsBuilder { #[inline(always)] fn default() -> Self { Self::new() } } /// Immutable options to customize writing integers. /// /// # Examples /// /// ```rust /// use lexical_parse_integer::options::Options; /// /// # pub fn main() { /// let options = Options::builder() /// .build() /// .unwrap(); /// # } /// ``` #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Options { /// Disable multi-digit optimizations. /// /// Using multi-digit optimizations allows parsing many digits /// from longer input strings at once which can dramatically /// improve performance (>70%) for long strings, but the /// increased branching can decrease performance for simple /// strings by 5-20%. Choose based on your inputs. no_multi_digit: bool, } impl Options { /// Create options with default values. #[must_use] #[inline(always)] pub const fn new() -> Self { Self::builder().build_unchecked() } // GETTERS /// Check if the options state is valid. #[inline(always)] pub const fn is_valid(&self) -> bool { self.rebuild().is_valid() } /// Get if we disable the use of multi-digit optimizations. #[inline(always)] pub const fn get_no_multi_digit(&self) -> bool { self.no_multi_digit } // SETTERS /// Set if we disable the use of multi-digit optimizations. #[inline(always)] pub fn no_multi_digit(&mut self, no_multi_digit: bool) { self.no_multi_digit = no_multi_digit; } // BUILDERS /// Get `OptionsBuilder` as a static function. #[inline(always)] pub const fn builder() -> OptionsBuilder { OptionsBuilder::new() } /// Create `OptionsBuilder` using existing values. #[inline(always)] pub const fn rebuild(&self) -> OptionsBuilder { OptionsBuilder { no_multi_digit: self.no_multi_digit, } } } impl Default for Options { #[inline(always)] fn default() -> Self { Self::new() } } impl ParseOptions for Options { #[inline(always)] fn is_valid(&self) -> bool { Self::is_valid(self) } } // PRE-DEFINED CONSTANTS // --------------------- /// Standard number format. #[rustfmt::skip] pub const STANDARD: Options = Options::new(); const_assert!(STANDARD.is_valid()); /// Options optimized for small numbers. #[rustfmt::skip] pub const SMALL_NUMBERS: Options = Options::builder() .no_multi_digit(true) .build_unchecked(); const_assert!(SMALL_NUMBERS.is_valid()); /// Options optimized for large numbers and long strings. #[rustfmt::skip] pub const LARGE_NUMBERS: Options = Options::builder() .no_multi_digit(false) .build_unchecked(); const_assert!(LARGE_NUMBERS.is_valid()); lexical-parse-integer-1.0.5/src/parse.rs000064400000000000000000000021451046102023000162640ustar 00000000000000//! Shared trait and methods for parsing integers. #![doc(hidden)] // Select the correct back-end. use lexical_util::num::Integer; use lexical_util::result::Result; use crate::algorithm::{algorithm_complete, algorithm_partial}; use crate::Options; /// Parse integer trait, implemented in terms of the optimized back-end. pub trait ParseInteger: Integer { /// Forward complete parser parameters to the backend. #[cfg_attr(not(feature = "compact"), inline(always))] fn parse_complete(bytes: &[u8], options: &Options) -> Result { algorithm_complete::<_, { FORMAT }>(bytes, options) } /// Forward partial parser parameters to the backend. #[cfg_attr(not(feature = "compact"), inline(always))] fn parse_partial(bytes: &[u8], options: &Options) -> Result<(Self, usize)> { algorithm_partial::<_, { FORMAT }>(bytes, options) } } macro_rules! parse_integer_impl { ($($t:ty)*) => ($( impl ParseInteger for $t {} )*) } parse_integer_impl! { u8 u16 u32 u64 u128 usize } parse_integer_impl! { i8 i16 i32 i64 i128 isize } lexical-parse-integer-1.0.5/tests/algorithm_tests.rs000064400000000000000000000171251046102023000207410ustar 00000000000000#![cfg(not(feature = "compact"))] mod util; use lexical_parse_integer::algorithm; use lexical_parse_integer::options::SMALL_NUMBERS; use lexical_util::format::STANDARD; use lexical_util::iterator::AsBytes; use proptest::prelude::*; #[cfg(feature = "power-of-two")] use util::from_radix; use crate::util::default_proptest_config; #[test] fn test_is_4digits() { let value: u32 = 0x31_32_33_34; #[cfg(feature = "power-of-two")] assert!(!algorithm::is_4digits::<{ from_radix(4) }>(value)); #[cfg(feature = "radix")] assert!(algorithm::is_4digits::<{ from_radix(5) }>(value)); assert!(algorithm::is_4digits::<{ STANDARD }>(value)); let value: u32 = 0x29_30_39_38; assert!(!algorithm::is_4digits::<{ STANDARD }>(value)); let value: u32 = 0x31_32_33_40; assert!(!algorithm::is_4digits::<{ STANDARD }>(value)); let value: u32 = 0x31_32_33_39; #[cfg(feature = "radix")] assert!(!algorithm::is_4digits::<{ from_radix(9) }>(value)); assert!(algorithm::is_4digits::<{ STANDARD }>(value)); } #[test] fn test_parse_4digits() { assert_eq!(algorithm::parse_4digits::<{ STANDARD }>(0x31_32_33_34), 4321); #[cfg(feature = "radix")] assert_eq!(algorithm::parse_4digits::<{ from_radix(5) }>(0x31_32_33_34), 586); assert_eq!(algorithm::parse_4digits::<{ STANDARD }>(0x36_37_38_39), 9876); } #[test] fn test_try_parse_4digits() { let parse = |bytes: &[u8]| { let mut digits = bytes.bytes::<{ STANDARD }>(); algorithm::try_parse_4digits::(&mut digits.integer_iter()) }; assert_eq!(parse(b"1234"), Some(1234)); assert_eq!(parse(b"123"), None); assert_eq!(parse(b"123\x00"), None); assert_eq!(parse(b"123."), None); assert_eq!(parse(b"123_"), None); assert_eq!(parse(b"1234_"), Some(1234)); } #[test] fn test_is_8digits() { let value: u64 = 0x31_32_33_34_35_36_37_38; #[cfg(feature = "power-of-two")] assert!(!algorithm::is_8digits::<{ from_radix(4) }>(value)); #[cfg(feature = "radix")] assert!(!algorithm::is_8digits::<{ from_radix(5) }>(value)); assert!(algorithm::is_8digits::<{ STANDARD }>(value)); let value: u64 = 0x29_30_31_32_33_34_35_36; assert!(!algorithm::is_8digits::<{ STANDARD }>(value)); let value: u64 = 0x30_31_32_33_34_35_36_40; assert!(!algorithm::is_8digits::<{ STANDARD }>(value)); let value: u64 = 0x31_32_33_34_35_36_37_39; #[cfg(feature = "radix")] assert!(!algorithm::is_8digits::<{ from_radix(9) }>(value)); assert!(algorithm::is_8digits::<{ STANDARD }>(value)); } #[test] fn test_parse_8digits() { // 10000000 let value: u64 = 0x30_30_30_30_30_30_30_31; assert_eq!(algorithm::parse_8digits::<{ STANDARD }>(value), 10000000); #[cfg(feature = "radix")] assert_eq!(algorithm::parse_8digits::<{ from_radix(5) }>(value), 78125); // 00000010 let value: u64 = 0x30_31_30_30_30_30_30_30; assert_eq!(algorithm::parse_8digits::<{ STANDARD }>(value), 10); #[cfg(feature = "radix")] assert_eq!(algorithm::parse_8digits::<{ from_radix(5) }>(value), 5); // 12344321 let value: u64 = 0x31_32_33_34_34_33_32_31; assert_eq!(algorithm::parse_8digits::<{ STANDARD }>(value), 12344321); #[cfg(feature = "power-of-two")] assert_eq!(algorithm::parse_8digits::<{ from_radix(8) }>(value), 2738385); #[cfg(feature = "radix")] { assert_eq!(algorithm::parse_8digits::<{ from_radix(9) }>(value), 6052420); assert_eq!(algorithm::parse_8digits::<{ from_radix(7) }>(value), 1120400); assert_eq!(algorithm::parse_8digits::<{ from_radix(6) }>(value), 402745); assert_eq!(algorithm::parse_8digits::<{ from_radix(5) }>(value), 121836); } } #[test] fn test_try_parse_8digits() { let parse = |bytes: &[u8]| { let mut digits = bytes.bytes::<{ STANDARD }>(); algorithm::try_parse_8digits::(&mut digits.integer_iter()) }; assert_eq!(parse(b"12345678"), Some(12345678)); assert_eq!(parse(b"1234567"), None); assert_eq!(parse(b"1234567\x00"), None); assert_eq!(parse(b"1234567."), None); assert_eq!(parse(b"1234567_"), None); assert_eq!(parse(b"12345678"), Some(12345678)); } #[cfg(feature = "power-of-two")] macro_rules! parse_radix { ($i:literal) => { |bytes: &[u8]| { algorithm::algorithm_partial::(bytes, &SMALL_NUMBERS) } }; } #[test] fn algorithm_test() { let parse_u32 = |bytes: &[u8]| algorithm::algorithm_partial::(bytes, &SMALL_NUMBERS); let parse_i32 = |bytes: &[u8]| algorithm::algorithm_partial::(bytes, &SMALL_NUMBERS); assert_eq!(parse_u32(b"12345"), Ok((12345, 5))); assert_eq!(parse_u32(b"+12345"), Ok((12345, 6))); assert_eq!(parse_u32(b"-12345"), Ok((0, 0))); assert_eq!(parse_i32(b"12345"), Ok((12345, 5))); assert_eq!(parse_i32(b"-12345"), Ok((-12345, 6))); assert_eq!(parse_i32(b"+12345"), Ok((12345, 6))); assert_eq!(parse_i32(b"+123.45"), Ok((123, 4))); // Need to try with other radixes here, especially to ensure no regressions with // #71. Issue: https://github.com/Alexhuszagh/rust-lexical/issues/71 #[cfg(feature = "power-of-two")] { // This should try to invoke `parse_4digits` since it's more than // 4 digits, and unsigned. assert_eq!(parse_radix!(4)(b"12345"), Ok((27, 3))); assert_eq!(parse_radix!(8)(b"12345"), Ok((5349, 5))); assert_eq!(parse_radix!(16)(b"12345"), Ok((74565, 5))); assert_eq!(parse_radix!(32)(b"12345"), Ok((1117317, 5))); } #[cfg(feature = "radix")] { assert_eq!(parse_radix!(6)(b"12345"), Ok((1865, 5))); assert_eq!(parse_radix!(12)(b"12345"), Ok((24677, 5))); assert_eq!(parse_radix!(24)(b"12345"), Ok((361253, 5))); } } #[test] fn algorithm_128_test() { let parse_u128 = |bytes: &[u8]| algorithm::algorithm_partial::(bytes, &SMALL_NUMBERS); let parse_i128 = |bytes: &[u8]| algorithm::algorithm_partial::(bytes, &SMALL_NUMBERS); assert_eq!(parse_u128(b"12345"), Ok((12345, 5))); assert_eq!(parse_u128(b"+12345"), Ok((12345, 6))); assert_eq!(parse_u128(b"-12345"), Ok((0, 0))); assert_eq!(parse_i128(b"12345"), Ok((12345, 5))); assert_eq!(parse_i128(b"-12345"), Ok((-12345, 6))); assert_eq!(parse_i128(b"+12345"), Ok((12345, 6))); assert_eq!(parse_i128(b"+123.45"), Ok((123, 4))); } proptest! { #![proptest_config(default_proptest_config())] #[test] fn parse_4digits_proptest( a in 0x30u32..0x39, b in 0x30u32..0x39, c in 0x30u32..0x39, d in 0x30u32..0x39, ) { let v = (a << 24) | (b << 16) | (c << 8) | d; let actual = algorithm::parse_4digits::<{ STANDARD }>(v); let expected = (a - 0x30) + 10 * (b - 0x30) + 100 * (c - 0x30) + 1000 * (d - 0x30); prop_assert_eq!(actual, expected); } #[test] fn parse_8digits_proptest( a in 0x30u64..0x39, b in 0x30u64..0x39, c in 0x30u64..0x39, d in 0x30u64..0x39, e in 0x30u64..0x39, f in 0x30u64..0x39, g in 0x30u64..0x39, h in 0x30u64..0x39, ) { let v1 = (a << 24) | (b << 16) | (c << 8) | d; let v2 = (e << 24) | (f << 16) | (g << 8) | h; let v = (v1 << 32) | v2; let actual = algorithm::parse_8digits::<{ STANDARD }>(v); let e1 = (a - 0x30) + 10 * (b - 0x30) + 100 * (c - 0x30) + 1000 * (d - 0x30); let e2 = (e - 0x30) + 10 * (f - 0x30) + 100 * (g - 0x30) + 1000 * (h - 0x30); let expected = e1 + 10000 * e2; prop_assert_eq!(actual, expected); } } lexical-parse-integer-1.0.5/tests/api_tests.rs000064400000000000000000000615031046102023000175230ustar 00000000000000mod util; use lexical_parse_integer::{FromLexical, FromLexicalWithOptions, Options}; use lexical_util::error::Error; #[cfg(feature = "format")] use lexical_util::format::NumberFormatBuilder; use lexical_util::format::STANDARD; use proptest::prelude::*; #[cfg(feature = "power-of-two")] use util::from_radix; use crate::util::default_proptest_config; #[test] fn u8_decimal_test() { assert_eq!(Ok(0), u8::from_lexical(b"0")); assert_eq!(Ok(127), u8::from_lexical(b"127")); assert_eq!(Ok(128), u8::from_lexical(b"128")); assert_eq!(Ok(255), u8::from_lexical(b"255")); assert_eq!(Err(Error::InvalidDigit(0)), u8::from_lexical(b"-1")); assert_eq!(Err(Error::InvalidDigit(1)), u8::from_lexical(b"1a")); } #[test] fn i8_decimal_test() { assert_eq!(Ok(0), i8::from_lexical(b"0")); assert_eq!(Ok(127), i8::from_lexical(b"127")); assert_eq!(Err(Error::Overflow(2)), i8::from_lexical(b"128")); assert_eq!(Err(Error::Overflow(2)), i8::from_lexical(b"255")); assert_eq!(Ok(-1), i8::from_lexical(b"-1")); assert_eq!(Err(Error::InvalidDigit(1)), i8::from_lexical(b"1a")); assert_eq!(Ok((1, 1)), i8::from_lexical_partial(b"1")); assert_eq!(Ok((1, 1)), i8::from_lexical_partial(b"1a")); assert_eq!(Ok((-1, 2)), i8::from_lexical_partial(b"-1")); assert_eq!(Ok((-1, 2)), i8::from_lexical_partial(b"-1a")); } #[test] fn u16_decimal_test() { assert_eq!(Ok(0), u16::from_lexical(b"0")); assert_eq!(Ok(32767), u16::from_lexical(b"32767")); assert_eq!(Ok(32768), u16::from_lexical(b"32768")); assert_eq!(Ok(65535), u16::from_lexical(b"65535")); assert_eq!(Err(Error::InvalidDigit(0)), u16::from_lexical(b"-1")); assert_eq!(Err(Error::InvalidDigit(1)), u16::from_lexical(b"1a")); } #[test] fn i16_decimal_test() { assert_eq!(Ok(0), i16::from_lexical(b"0")); assert_eq!(Ok(32767), i16::from_lexical(b"32767")); assert_eq!(Err(Error::Overflow(4)), i16::from_lexical(b"32768")); assert_eq!(Err(Error::Overflow(4)), i16::from_lexical(b"65535")); assert_eq!(Ok(-1), i16::from_lexical(b"-1")); assert_eq!(Err(Error::InvalidDigit(1)), i16::from_lexical(b"1a")); } #[test] fn u32_decimal_test() { assert_eq!(Ok(0), u32::from_lexical(b"0")); assert_eq!(Ok(2147483647), u32::from_lexical(b"2147483647")); assert_eq!(Ok(2147483648), u32::from_lexical(b"2147483648")); assert_eq!(Ok(4294967295), u32::from_lexical(b"4294967295")); assert_eq!(Err(Error::InvalidDigit(0)), u32::from_lexical(b"-1")); assert_eq!(Err(Error::InvalidDigit(1)), u32::from_lexical(b"1a")); } #[test] fn i32_decimal_test() { assert_eq!(Ok(0), i32::from_lexical(b"0")); assert_eq!(Ok(2147483647), i32::from_lexical(b"2147483647")); assert_eq!(Err(Error::Overflow(9)), i32::from_lexical(b"2147483648")); assert_eq!(Err(Error::Overflow(9)), i32::from_lexical(b"4294967295")); assert_eq!(Ok(-1), i32::from_lexical(b"-1")); assert_eq!(Err(Error::InvalidDigit(1)), i32::from_lexical(b"1a")); } #[test] fn u64_decimal_test() { assert_eq!(Ok(0), u64::from_lexical(b"0")); assert_eq!(Ok(9223372036854775807), u64::from_lexical(b"9223372036854775807")); assert_eq!(Ok(9223372036854775808), u64::from_lexical(b"9223372036854775808")); assert_eq!(Ok(18446744073709551615), u64::from_lexical(b"18446744073709551615")); assert_eq!(Err(Error::InvalidDigit(0)), u64::from_lexical(b"-1")); assert_eq!(Err(Error::InvalidDigit(1)), u64::from_lexical(b"1a")); } #[test] fn i64_decimal_test() { assert_eq!(Ok(0), i64::from_lexical(b"0")); assert_eq!(Ok(9223372036854775807), i64::from_lexical(b"9223372036854775807")); assert_eq!(Err(Error::Overflow(18)), i64::from_lexical(b"9223372036854775808")); assert_eq!(Err(Error::Overflow(19)), i64::from_lexical(b"18446744073709551615")); assert_eq!(Ok(-1), i64::from_lexical(b"-1")); assert_eq!(Err(Error::InvalidDigit(1)), i64::from_lexical(b"1a")); // Add tests discovered via fuzzing. This won't necessarily be the // proper index, since we use multi-digit parsing. assert!(i64::from_lexical(b"406260572150672006000066000000060060007667760000000000000000000+00000006766767766666767665670000000000000000000000666").err().unwrap().is_overflow()); assert!(i64::from_lexical(b"406260572150672006000066000000060060007667760000000000000000000") .err() .unwrap() .is_overflow()); } #[test] fn u128_decimal_test() { assert_eq!(Ok(0), u128::from_lexical(b"0")); assert_eq!( Ok(170141183460469231731687303715884105727), u128::from_lexical(b"170141183460469231731687303715884105727") ); assert_eq!( Ok(170141183460469231731687303715884105728), u128::from_lexical(b"170141183460469231731687303715884105728") ); assert_eq!( Ok(340282366920938463463374607431768211455), u128::from_lexical(b"340282366920938463463374607431768211455") ); assert_eq!(Err(Error::InvalidDigit(0)), u128::from_lexical(b"-1")); assert_eq!(Err(Error::InvalidDigit(1)), u128::from_lexical(b"1a")); } #[test] fn i128_decimal_test() { assert_eq!(Ok(0), i128::from_lexical(b"0")); assert_eq!( Ok(170141183460469231731687303715884105727), i128::from_lexical(b"170141183460469231731687303715884105727") ); assert_eq!( Err(Error::Overflow(38)), i128::from_lexical(b"170141183460469231731687303715884105728") ); assert_eq!( Err(Error::Overflow(38)), i128::from_lexical(b"340282366920938463463374607431768211455") ); assert_eq!(Ok(-1), i128::from_lexical(b"-1")); assert_eq!(Err(Error::InvalidDigit(1)), i128::from_lexical(b"1a")); } #[test] fn double_sign_test() { assert_eq!(Err(Error::InvalidDigit(1)), i16::from_lexical(b"+-0000")); assert_eq!(Err(Error::InvalidDigit(1)), i128::from_lexical(b"+-0000")); } #[test] fn options_test() { let options = Options::new(); assert_eq!(Ok(0), i128::from_lexical_with_options::(b"0", &options)); } #[test] #[cfg(feature = "power-of-two")] fn i32_binary_test() { let options = Options::new(); const FORMAT: u128 = from_radix(2); assert_eq!(i32::from_lexical_with_options::(b"11", &options), Ok(3)); assert_eq!(i32::from_lexical_with_options::(b"-11", &options), Ok(-3)); } #[cfg(feature = "radix")] fn radix_to_u32(bytes: &[u8], expected: u32) { let options = Options::new(); let result = u32::from_lexical_with_options::<{ FORMAT }>(bytes, &options); assert_eq!(result, Ok(expected)); } #[test] #[cfg(feature = "radix")] fn radix_test() { radix_to_u32::<{ from_radix(2) }>(b"100101", 37); radix_to_u32::<{ from_radix(3) }>(b"1101", 37); radix_to_u32::<{ from_radix(4) }>(b"211", 37); radix_to_u32::<{ from_radix(5) }>(b"122", 37); radix_to_u32::<{ from_radix(6) }>(b"101", 37); radix_to_u32::<{ from_radix(7) }>(b"52", 37); radix_to_u32::<{ from_radix(8) }>(b"45", 37); radix_to_u32::<{ from_radix(9) }>(b"41", 37); radix_to_u32::<{ from_radix(10) }>(b"37", 37); radix_to_u32::<{ from_radix(11) }>(b"34", 37); radix_to_u32::<{ from_radix(12) }>(b"31", 37); radix_to_u32::<{ from_radix(13) }>(b"2B", 37); radix_to_u32::<{ from_radix(14) }>(b"29", 37); radix_to_u32::<{ from_radix(15) }>(b"27", 37); radix_to_u32::<{ from_radix(16) }>(b"25", 37); radix_to_u32::<{ from_radix(17) }>(b"23", 37); radix_to_u32::<{ from_radix(18) }>(b"21", 37); radix_to_u32::<{ from_radix(19) }>(b"1I", 37); radix_to_u32::<{ from_radix(20) }>(b"1H", 37); radix_to_u32::<{ from_radix(21) }>(b"1G", 37); radix_to_u32::<{ from_radix(22) }>(b"1F", 37); radix_to_u32::<{ from_radix(23) }>(b"1E", 37); radix_to_u32::<{ from_radix(24) }>(b"1D", 37); radix_to_u32::<{ from_radix(25) }>(b"1C", 37); radix_to_u32::<{ from_radix(26) }>(b"1B", 37); radix_to_u32::<{ from_radix(27) }>(b"1A", 37); radix_to_u32::<{ from_radix(28) }>(b"19", 37); radix_to_u32::<{ from_radix(29) }>(b"18", 37); radix_to_u32::<{ from_radix(30) }>(b"17", 37); radix_to_u32::<{ from_radix(31) }>(b"16", 37); radix_to_u32::<{ from_radix(32) }>(b"15", 37); radix_to_u32::<{ from_radix(33) }>(b"14", 37); radix_to_u32::<{ from_radix(34) }>(b"13", 37); radix_to_u32::<{ from_radix(35) }>(b"12", 37); radix_to_u32::<{ from_radix(36) }>(b"11", 37); } #[test] #[cfg(feature = "format")] fn i32_no_leading_zeros_test() { let options = Options::new(); const FORMAT: u128 = NumberFormatBuilder::new().no_integer_leading_zeros(true).build(); assert!(i32::from_lexical_with_options::(b"1", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"0", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"01", &options).is_err()); assert!(i32::from_lexical_with_options::(b"10", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"010", &options).is_err()); } #[test] #[cfg(feature = "format")] fn i32_integer_internal_digit_separator_test() { let options = Options::new(); const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(std::num::NonZeroU8::new(b'_')) .integer_internal_digit_separator(true) .build(); assert!(i32::from_lexical_with_options::(b"3_1", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"_31", &options).is_err()); assert!(i32::from_lexical_with_options::(b"31_", &options).is_err()); } #[test] #[cfg(feature = "format")] fn i32_integer_leading_digit_separator_test() { let options = Options::new(); const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(std::num::NonZeroU8::new(b'_')) .integer_leading_digit_separator(true) .build(); assert!(i32::from_lexical_with_options::(b"3_1", &options).is_err()); assert!(i32::from_lexical_with_options::(b"_31", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"31_", &options).is_err()); } #[test] #[cfg(feature = "format")] fn i32_integer_trailing_digit_separator_test() { let options = Options::new(); const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(std::num::NonZeroU8::new(b'_')) .integer_trailing_digit_separator(true) .build(); assert!(i32::from_lexical_with_options::(b"3_1", &options).is_err()); assert!(i32::from_lexical_with_options::(b"_31", &options).is_err()); assert!(i32::from_lexical_with_options::(b"31_", &options).is_ok()); } #[test] #[cfg(feature = "format")] fn i32_integer_consecutive_digit_separator_test() { let options = Options::new(); const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(std::num::NonZeroU8::new(b'_')) .integer_internal_digit_separator(true) .integer_consecutive_digit_separator(true) .build(); assert!(i32::from_lexical_with_options::(b"3_1", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"3__1", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"_31", &options).is_err()); assert!(i32::from_lexical_with_options::(b"31_", &options).is_err()); } #[test] #[cfg(feature = "format")] fn i32_json_no_leading_zero() { let options = Options::new(); use lexical_util::format::JSON; assert!(i32::from_lexical_with_options::<{ JSON }>(b"12", &options).is_ok()); assert!(i32::from_lexical_with_options::<{ JSON }>(b"-12", &options).is_ok()); assert!(i32::from_lexical_with_options::<{ JSON }>(b"012", &options).is_err()); assert!(i32::from_lexical_with_options::<{ JSON }>(b"-012", &options).is_err()); } #[test] #[cfg(all(feature = "power-of-two", feature = "format"))] fn base_prefix_test() { use core::num; const FORMAT: u128 = NumberFormatBuilder::new().base_prefix(num::NonZeroU8::new(b'x')).build(); let options = Options::new(); assert!(i32::from_lexical_with_options::(b"0x", &options).is_err()); assert!(i32::from_lexical_with_options::(b"-0x", &options).is_err()); assert!(i32::from_lexical_with_options::(b"-0x1", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"0x12", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"12", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"-0x12", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"0x-12", &options).is_err()); assert!(i32::from_lexical_with_options::(b"012", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"-0x012", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"-0x012h", &options).is_err()); assert!(i32::from_lexical_with_options::(b"-0x012h ", &options).is_err()); assert!(i32::from_lexical_partial_with_options::(b"-0x012h", &options).is_ok()); assert!(i32::from_lexical_partial_with_options::(b"-0x012h ", &options).is_ok()); } #[test] #[cfg(all(feature = "power-of-two", feature = "format"))] fn base_suffix_test() { use core::num; const FORMAT: u128 = NumberFormatBuilder::new().base_suffix(num::NonZeroU8::new(b'h')).build(); let options = Options::new(); assert!(i32::from_lexical_with_options::(b"h", &options).is_err()); assert!(i32::from_lexical_with_options::(b"-h", &options).is_err()); assert!(i32::from_lexical_with_options::(b"-1h", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"12h", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"12", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"-12h", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"0x-12", &options).is_err()); assert!(i32::from_lexical_with_options::(b"0x12", &options).is_err()); assert!(i32::from_lexical_with_options::(b"012h", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"-012", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"-0x012h", &options).is_err()); assert!(i32::from_lexical_with_options::(b"-0x012h ", &options).is_err()); assert!(i32::from_lexical_partial_with_options::(b"-0x012h", &options).is_ok()); assert!(i32::from_lexical_partial_with_options::(b"-0x012h ", &options).is_ok()); } #[test] #[cfg(all(feature = "power-of-two", feature = "format"))] fn base_prefix_and_suffix_test() { use core::num; const FORMAT: u128 = NumberFormatBuilder::new() .base_prefix(num::NonZeroU8::new(b'x')) .base_suffix(num::NonZeroU8::new(b'h')) .build(); let options = Options::new(); assert!(i32::from_lexical_with_options::(b"+3h", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"+0x3", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"+0x3h", &options).is_ok()); assert!(i32::from_lexical_with_options::(b"+0x3h ", &options).is_err()); assert!(i32::from_lexical_with_options::(b"+0xh", &options).is_err()); assert!(i32::from_lexical_with_options::(b"+h", &options).is_err()); assert!(i32::from_lexical_with_options::(b"+0x", &options).is_err()); } macro_rules! is_error { ($result:expr, $check:ident) => {{ let result = $result; prop_assert!(result.is_err()); let err = result.err().unwrap(); prop_assert!(err.$check()); }}; } macro_rules! is_invalid_digit { ($result:expr) => { is_error!($result, is_invalid_digit) }; } macro_rules! is_empty { ($result:expr) => { is_error!($result, is_empty) }; } macro_rules! is_overflow { ($result:expr) => { is_error!($result, is_overflow) }; } macro_rules! is_underflow { ($result:expr) => { is_error!($result, is_underflow) }; } macro_rules! is_invalid_digit_match { ($result:expr, $p1:pat_param $(| $prest:pat_param)*) => {{ let result = $result; prop_assert!(result.is_err()); let err = result.err().unwrap(); prop_assert!(err.is_invalid_digit()); prop_assert!(matches!(*err.index().unwrap(), $p1 $(| $prest)*)); }}; } proptest! { #![proptest_config(default_proptest_config())] #[test] #[cfg(feature = "power-of-two")] fn i32_binary_roundtrip_display_proptest(i in i32::MIN..i32::MAX) { let options = Options::new(); const FORMAT: u128 = from_radix(2); let digits = if i < 0 { format!("-{:b}", (i as i64).wrapping_neg()) } else { format!("{:b}", i) }; let result = i32::from_lexical_with_options::(digits.as_bytes(), &options); prop_assert_eq!(i, result.unwrap()); } #[test] fn u8_invalid_proptest(i in r"[+]?[0-9]{2}\D") { is_invalid_digit_match!(u8::from_lexical(i.as_bytes()), 2 | 3); } #[test] fn u8_overflow_proptest(i in r"[+]?[1-9][0-9]{3}") { is_overflow!(u8::from_lexical(i.as_bytes())); } #[test] fn u8_negative_proptest(i in r"[-][1-9][0-9]{2}") { is_invalid_digit!(u8::from_lexical(i.as_bytes())); } #[test] fn u8_double_sign_proptest(i in r"[+]{2}[0-9]{2}") { is_invalid_digit_match!(u8::from_lexical(i.as_bytes()), 1); } #[test] fn u8_sign_only_proptest(i in r"[+]") { is_empty!(u8::from_lexical(i.as_bytes())); } #[test] fn u8_trailing_digits_proptest(i in r"[+]?[0-9]{2}\D[0-9]{2}") { is_invalid_digit_match!(u8::from_lexical(i.as_bytes()), 2 | 3); } #[test] fn i8_invalid_proptest(i in r"[+-]?[0-9]{2}\D") { is_invalid_digit_match!(i8::from_lexical(i.as_bytes()), 2 | 3); } #[test] fn i8_overflow_proptest(i in r"[+]?[1-9][0-9]{3}") { is_overflow!(i8::from_lexical(i.as_bytes())); } #[test] fn i8_underflow_proptest(i in r"[-][1-9][0-9]{3}") { is_underflow!(i8::from_lexical(i.as_bytes())); } #[test] fn i8_double_sign_proptest(i in r"[+-]{2}[0-9]{2}") { is_invalid_digit_match!(i8::from_lexical(i.as_bytes()), 1); } #[test] fn i8_sign_only_proptest(i in r"[+-]") { is_empty!(i8::from_lexical(i.as_bytes())); } #[test] fn i8_trailing_digits_proptest(i in r"[+-]?[0-9]{2}\D[0-9]{2}") { is_invalid_digit_match!(i8::from_lexical(i.as_bytes()), 2 | 3); } #[test] fn u16_invalid_proptest(i in r"[+]?[0-9]{4}\D") { is_invalid_digit_match!(u16::from_lexical(i.as_bytes()), 4 | 5); } #[test] fn u16_overflow_proptest(i in r"[+]?[1-9][0-9]{5}") { is_overflow!(u16::from_lexical(i.as_bytes())); } #[test] fn u16_negative_proptest(i in r"[-][1-9][0-9]{4}") { is_invalid_digit!(u16::from_lexical(i.as_bytes())); } #[test] fn u16_double_sign_proptest(i in r"[+]{2}[0-9]{4}") { is_invalid_digit_match!(u16::from_lexical(i.as_bytes()), 1); } #[test] fn u16_sign_only_proptest(i in r"[+]") { is_empty!(u16::from_lexical(i.as_bytes())); } #[test] fn u16_trailing_digits_proptest(i in r"[+]?[0-9]{4}\D[0-9]{2}") { is_invalid_digit_match!(u16::from_lexical(i.as_bytes()), 4 | 5); } #[test] fn i16_invalid_proptest(i in r"[+-]?[0-9]{4}\D") { is_invalid_digit_match!(i16::from_lexical(i.as_bytes()), 4 | 5); } #[test] fn i16_overflow_proptest(i in r"[+]?[1-9][0-9]{5}") { is_overflow!(i16::from_lexical(i.as_bytes())); } #[test] fn i16_underflow_proptest(i in r"[-][1-9][0-9]{5}") { is_underflow!(i16::from_lexical(i.as_bytes())); } #[test] fn i16_double_sign_proptest(i in r"[+-]{2}[0-9]{4}") { is_invalid_digit_match!(i16::from_lexical(i.as_bytes()), 1); } #[test] fn i16_sign_only_proptest(i in r"[+-]") { is_empty!(i16::from_lexical(i.as_bytes())); } #[test] fn i16_trailing_digits_proptest(i in r"[+-]?[0-9]{4}\D[0-9]{2}") { is_invalid_digit_match!(i16::from_lexical(i.as_bytes()), 4 | 5); } #[test] fn u32_invalid_proptest(i in r"[+]?[0-9]{9}\D") { is_invalid_digit_match!(u32::from_lexical(i.as_bytes()), 9 | 10); } #[test] fn u32_overflow_proptest(i in r"[+]?[1-9][0-9]{10}") { is_overflow!(u32::from_lexical(i.as_bytes())); } #[test] fn u32_negative_proptest(i in r"[-][1-9][0-9]{9}") { is_invalid_digit!(u32::from_lexical(i.as_bytes())); } #[test] fn u32_double_sign_proptest(i in r"[+]{2}[0-9]{9}") { is_invalid_digit_match!(u32::from_lexical(i.as_bytes()), 1); } #[test] fn u32_sign_only_proptest(i in r"[+]") { is_empty!(u32::from_lexical(i.as_bytes())); } #[test] fn u32_trailing_digits_proptest(i in r"[+]?[0-9]{9}\D[0-9]{2}") { is_invalid_digit_match!(u32::from_lexical(i.as_bytes()), 9 | 10); } #[test] fn i32_invalid_proptest(i in r"[+-]?[0-9]{9}\D") { is_invalid_digit_match!(i32::from_lexical(i.as_bytes()), 9 | 10); } #[test] fn i32_overflow_proptest(i in r"[+]?[1-9][0-9]{10}") { is_overflow!(i32::from_lexical(i.as_bytes())); } #[test] fn i32_underflow_proptest(i in r"-[1-9][0-9]{10}") { is_underflow!(i32::from_lexical(i.as_bytes())); } #[test] fn i32_double_sign_proptest(i in r"[+-]{2}[0-9]{9}") { is_invalid_digit_match!(i32::from_lexical(i.as_bytes()), 1); } #[test] fn i32_sign_only_proptest(i in r"[+-]") { is_empty!(i32::from_lexical(i.as_bytes())); } #[test] fn i32_trailing_digits_proptest(i in r"[+-]?[0-9]{9}\D[0-9]{2}") { is_invalid_digit_match!(i32::from_lexical(i.as_bytes()), 9 | 10); } #[test] fn u64_invalid_proptest(i in r"[+]?[0-9]{19}\D") { is_invalid_digit_match!(u64::from_lexical(i.as_bytes()), 19 | 20); } #[test] fn u64_overflow_proptest(i in r"[+]?[1-9][0-9]{21}") { is_overflow!(u64::from_lexical(i.as_bytes())); } #[test] fn u64_negative_proptest(i in r"[-][1-9][0-9]{21}") { is_invalid_digit!(u64::from_lexical(i.as_bytes())); } #[test] fn u64_double_sign_proptest(i in r"[+]{2}[0-9]{19}") { is_invalid_digit_match!(u64::from_lexical(i.as_bytes()), 1); } #[test] fn u64_sign_only_proptest(i in r"[+]") { is_empty!(u64::from_lexical(i.as_bytes())); } #[test] fn u64_trailing_digits_proptest(i in r"[+]?[0-9]{19}\D[0-9]{2}") { is_invalid_digit_match!(u64::from_lexical(i.as_bytes()), 19 | 20); } #[test] fn i64_invalid_proptest(i in r"[+-]?[0-9]{18}\D") { is_invalid_digit_match!(i64::from_lexical(i.as_bytes()), 18 | 19); } #[test] fn i64_overflow_proptest(i in r"[+]?[1-9][0-9]{19}") { is_overflow!(i64::from_lexical(i.as_bytes())); } #[test] fn i64_underflow_proptest(i in r"-[1-9][0-9]{19}") { is_underflow!(i64::from_lexical(i.as_bytes())); } #[test] fn i64_double_sign_proptest(i in r"[+-]{2}[0-9]{18}") { is_invalid_digit_match!(i64::from_lexical(i.as_bytes()), 1); } #[test] fn i64_sign_only_proptest(i in r"[+-]") { is_empty!(i64::from_lexical(i.as_bytes())); } #[test] fn i64_trailing_digits_proptest(i in r"[+-]?[0-9]{18}\D[0-9]{2}") { is_invalid_digit_match!(i64::from_lexical(i.as_bytes()), 18 | 19); } #[test] fn u128_invalid_proptest(i in r"[+]?[0-9]{38}\D") { is_invalid_digit_match!(u128::from_lexical(i.as_bytes()), 38 | 39); } #[test] fn u128_overflow_proptest(i in r"[+]?[1-9][0-9]{39}") { is_overflow!(u128::from_lexical(i.as_bytes())); } #[test] fn u128_negative_proptest(i in r"[-][1-9][0-9]{39}") { is_invalid_digit!(u128::from_lexical(i.as_bytes())); } #[test] fn u128_double_sign_proptest(i in r"[+]{2}[0-9]{38}") { is_invalid_digit_match!(u128::from_lexical(i.as_bytes()), 1); } #[test] fn u128_sign_only_proptest(i in r"[+]") { is_empty!(u128::from_lexical(i.as_bytes())); } #[test] fn u128_trailing_digits_proptest(i in r"[+]?[0-9]{38}\D[0-9]{2}") { is_invalid_digit_match!(u128::from_lexical(i.as_bytes()), 38 | 39); } #[test] fn i128_invalid_proptest(i in r"[+-]?[0-9]{38}\D") { is_invalid_digit_match!(i128::from_lexical(i.as_bytes()), 38 | 39); } #[test] fn i128_overflow_proptest(i in r"[+]?[1-9][0-9]{39}") { is_overflow!(i128::from_lexical(i.as_bytes())); } #[test] fn i128_underflow_proptest(i in r"-[1-9][0-9]{39}") { is_underflow!(i128::from_lexical(i.as_bytes())); } #[test] fn i128_double_sign_proptest(i in r"[+-]{2}[0-9]{38}") { is_invalid_digit_match!(i128::from_lexical(i.as_bytes()), 1); } #[test] fn i128_sign_only_proptest(i in r"[+-]") { is_empty!(i128::from_lexical(i.as_bytes())); } #[test] fn i128_trailing_digits_proptest(i in r"[+-]?[0-9]{38}\D[0-9]{2}") { is_invalid_digit_match!(i128::from_lexical(i.as_bytes()), 38 | 39); } } lexical-parse-integer-1.0.5/tests/issue_91_tests.rs000064400000000000000000000021001046102023000203770ustar 00000000000000use lexical_parse_integer::FromLexical; #[test] fn issue_91_test() { // Derived from: // https://github.com/Alexhuszagh/rust-lexical/issues/91 assert!(u8::from_lexical(b"354").is_err()); assert!(u8::from_lexical(b"355").is_err()); assert!(u8::from_lexical(b"356").is_err()); assert!(u8::from_lexical(b"357").is_err()); assert!(u8::from_lexical(b"358").is_err()); assert!(u8::from_lexical(b"510").is_err()); assert!(u8::from_lexical(b"511").is_err()); assert!(u8::from_lexical(b"512").is_err()); assert!(u8::from_lexical(b"513").is_err()); assert!(u8::from_lexical(b"514").is_err()); assert!(u8::from_lexical(b"612").is_err()); assert!(u8::from_lexical(b"999").is_err()); assert!(u8::from_lexical(b"1000").is_err()); let n = u32::MAX as u64 + 1_000_000_000; assert!(u32::from_lexical((n - 1).to_string().as_bytes()).is_err()); assert!(u32::from_lexical(n.to_string().as_bytes()).is_err()); assert!(u32::from_lexical((n + 1).to_string().as_bytes()).is_err()); assert!(u8::from_lexical(b"357").is_err()); } lexical-parse-integer-1.0.5/tests/issue_96_tests.rs000064400000000000000000000327571046102023000204310ustar 00000000000000#![cfg(feature = "format")] use core::num; use lexical_parse_integer::{ Error, FromLexical, FromLexicalWithOptions, NumberFormatBuilder, Options, }; #[test] fn issue_96_test() { let opts = Options::new(); const NO_CONSECUTIVE: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) .build(); const CONSECUTIVE: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) .build(); const NO_LEADING: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(false) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) .build(); let result = i64::from_lexical(b"_-1234"); assert_eq!(result, Err(Error::InvalidDigit(0))); // NOTE: We need to make sure we're not skipping digit separators before the // sign, which is never allowed. let result = u64::from_lexical_with_options::(b"_-1234", &opts); assert_eq!(result, Err(Error::InvalidDigit(1))); let result = i64::from_lexical_with_options::(b"_-1234", &opts); assert_eq!(result, Err(Error::InvalidDigit(1))); let result = i64::from_lexical_with_options::(b"^-1234", &opts); assert_eq!(result, Err(Error::InvalidDigit(0))); // NOTE: This uis correct, since it's "trailing" let result = i64::from_lexical_with_options::(b"_-1234", &opts); assert_eq!(result, Err(Error::InvalidDigit(1))); let result = i64::from_lexical_with_options::(b"_1234", &opts); assert_eq!(result, Err(Error::InvalidDigit(0))); let result = i64::from_lexical_with_options::(b"X1234", &opts); assert_eq!(result, Err(Error::InvalidDigit(0))); let result = i64::from_lexical_with_options::(b"__1__234__", &opts); assert_eq!(result, Err(Error::InvalidDigit(0))); let result = i64::from_lexical_with_options::(b"__1__234__", &opts); assert_eq!(result, Ok(1234)); } #[test] fn issue_96_i_test() { let opts = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) .consecutive_digit_separator(false) .required_digits(true) .build(); let result = i64::from_lexical_partial_with_options::(b"", &opts); assert_eq!(result, Err(Error::Empty(0))); let result = i64::from_lexical_partial_with_options::(b"_", &opts); assert_eq!(result, Err(Error::Empty(0))); let result = i64::from_lexical_partial_with_options::(b"+_", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"_1_", &opts); assert_eq!(result, Err(Error::Empty(0))); let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); assert_eq!(result, Err(Error::Empty(0))); let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); assert_eq!(result, Ok((11, 3))); let result = i64::from_lexical_partial_with_options::(b"1_1_23", &opts); assert_eq!(result, Ok((1123, 6))); let result = i64::from_lexical_partial_with_options::(b"1_1__23", &opts); assert_eq!(result, Ok((11, 3))); let result = i64::from_lexical_partial_with_options::(b"1_1_23_", &opts); assert_eq!(result, Ok((1123, 6))); let result = i64::from_lexical_partial_with_options::(b"1_1_23.", &opts); assert_eq!(result, Ok((1123, 6))); } #[test] fn issue_96_l_test() { let opts = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .consecutive_digit_separator(false) .build(); let result = i64::from_lexical_partial_with_options::(b"", &opts); assert_eq!(result, Err(Error::Empty(0))); let result = i64::from_lexical_partial_with_options::(b"_", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"+_", &opts); assert_eq!(result, Err(Error::Empty(2))); let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); assert_eq!(result, Ok((1, 2))); let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); assert_eq!(result, Ok((1, 3))); let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); assert_eq!(result, Err(Error::Empty(1))); } #[test] fn issue_96_t_test() { let opts = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .trailing_digit_separator(true) .consecutive_digit_separator(false) .build(); let result = i64::from_lexical_partial_with_options::(b"", &opts); assert_eq!(result, Err(Error::Empty(0))); let result = i64::from_lexical_partial_with_options::(b"_", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"+_", &opts); assert_eq!(result, Err(Error::Empty(2))); let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); assert_eq!(result, Err(Error::Empty(0))); let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"+123_", &opts); assert_eq!(result, Ok((123, 5))); let result = i64::from_lexical_partial_with_options::(b"+123__", &opts); assert_eq!(result, Ok((123, 4))); } #[test] fn issue_96_il_test() { let opts = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) .leading_digit_separator(true) .consecutive_digit_separator(false) .build(); let result = i64::from_lexical_partial_with_options::(b"", &opts); assert_eq!(result, Err(Error::Empty(0))); let result = i64::from_lexical_partial_with_options::(b"_", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"+_", &opts); assert_eq!(result, Err(Error::Empty(2))); let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); assert_eq!(result, Ok((123, 5))); let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); assert_eq!(result, Ok((123, 6))); let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); assert_eq!(result, Ok((11, 3))); let result = i64::from_lexical_partial_with_options::(b"1_1_", &opts); assert_eq!(result, Ok((11, 3))); let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"+123_", &opts); assert_eq!(result, Ok((123, 4))); let result = i64::from_lexical_partial_with_options::(b"+123__", &opts); assert_eq!(result, Ok((123, 4))); } #[test] fn issue_96_it_test() { let opts = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) .build(); let result = i64::from_lexical_partial_with_options::(b"", &opts); assert_eq!(result, Err(Error::Empty(0))); let result = i64::from_lexical_partial_with_options::(b"_", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"+_", &opts); assert_eq!(result, Err(Error::Empty(2))); let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); assert_eq!(result, Err(Error::Empty(0))); let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); assert_eq!(result, Ok((11, 3))); let result = i64::from_lexical_partial_with_options::(b"1_1_", &opts); assert_eq!(result, Ok((11, 4))); let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"+123_", &opts); assert_eq!(result, Ok((123, 5))); let result = i64::from_lexical_partial_with_options::(b"+123__", &opts); assert_eq!(result, Ok((123, 4))); } #[test] fn issue_96_lt_test() { let opts = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) .build(); let result = i64::from_lexical_partial_with_options::(b"", &opts); assert_eq!(result, Err(Error::Empty(0))); let result = i64::from_lexical_partial_with_options::(b"_", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"+_", &opts); assert_eq!(result, Err(Error::Empty(2))); let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); assert_eq!(result, Ok((1, 2))); let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); assert_eq!(result, Ok((1, 3))); let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"1_1_", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"_11_", &opts); assert_eq!(result, Ok((11, 4))); let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); assert_eq!(result, Err(Error::Empty(1))); let result = i64::from_lexical_partial_with_options::(b"+123_", &opts); assert_eq!(result, Ok((123, 5))); let result = i64::from_lexical_partial_with_options::(b"+123__", &opts); assert_eq!(result, Ok((123, 4))); } #[test] fn issue_96_no_required_test() { let opts = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) .required_digits(false) .build(); let result = i64::from_lexical_partial_with_options::(b"", &opts); assert_eq!(result, Ok((0, 0))); let result = i64::from_lexical_partial_with_options::(b"_", &opts); assert_eq!(result, Ok((0, 1))); let result = i64::from_lexical_partial_with_options::(b"+_", &opts); assert_eq!(result, Ok((0, 2))); let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); assert_eq!(result, Ok((1, 2))); let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); assert_eq!(result, Ok((1, 3))); let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"1_1_", &opts); assert_eq!(result, Ok((1, 1))); let result = i64::from_lexical_partial_with_options::(b"_11_", &opts); assert_eq!(result, Ok((11, 4))); let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); assert_eq!(result, Ok((0, 1))); let result = i64::from_lexical_partial_with_options::(b"+123_", &opts); assert_eq!(result, Ok((123, 5))); let result = i64::from_lexical_partial_with_options::(b"+123__", &opts); assert_eq!(result, Ok((123, 4))); } lexical-parse-integer-1.0.5/tests/issue_98_tests.rs000064400000000000000000000051171046102023000204210ustar 00000000000000#![cfg(all(feature = "power-of-two", feature = "format"))] use lexical_parse_integer::FromLexicalWithOptions; use lexical_parse_integer::NumberFormatBuilder; use lexical_parse_integer::Options; use lexical_util::error::Error; #[test] fn issue_98_test() { const DECIMAL_FORMAT: u128 = NumberFormatBuilder::new() .required_digits(true) .no_positive_mantissa_sign(false) .no_special(true) .no_integer_leading_zeros(true) .no_float_leading_zeros(false) .build(); let result = i64::from_lexical_with_options::(b"1.1.0", &Options::new()); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::InvalidDigit(1)); assert_eq!( i64::from_lexical_partial_with_options::(b"1.1.0", &Options::new()), Ok((1, 1)) ); let result = i64::from_lexical_with_options::(b"1.1", &Options::new()); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::InvalidDigit(1)); assert!(i64::from_lexical_with_options::(b"1.1", &Options::new()).is_err()); assert_eq!( i64::from_lexical_partial_with_options::(b"1.1", &Options::new()), Ok((1, 1)) ); let result = i64::from_lexical_with_options::(b"0.1.0", &Options::new()); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::InvalidDigit(1)); assert_eq!( i64::from_lexical_partial_with_options::(b"0.1.0", &Options::new()), Ok((0, 1)) ); let result = i64::from_lexical_with_options::(b"0.1", &Options::new()); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::InvalidDigit(1)); assert!(i64::from_lexical_with_options::(b"0.1", &Options::new()).is_err()); assert_eq!( i64::from_lexical_partial_with_options::(b"0.1", &Options::new()), Ok((0, 1)) ); let result = i64::from_lexical_with_options::(b"01.1", &Options::new()); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::InvalidLeadingZeros(0)); let result = i64::from_lexical_with_options::(b"00.1", &Options::new()); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::InvalidLeadingZeros(0)); assert_eq!( i64::from_lexical_partial_with_options::(b"10.1", &Options::new()), Ok((10, 2)) ); assert_eq!( i64::from_lexical_partial_with_options::(b"11.1", &Options::new()), Ok((11, 2)) ); } lexical-parse-integer-1.0.5/tests/options_tests.rs000064400000000000000000000011371046102023000204420ustar 00000000000000use lexical_parse_integer::options::{Options, OptionsBuilder}; #[test] fn options_tests() { let builder = OptionsBuilder::new(); assert!(builder.is_valid()); assert!(builder.build_unchecked().is_valid()); assert!(OptionsBuilder::default().is_valid()); let options: Options = Options::new(); assert!(options.is_valid()); assert_eq!(options, Options::default()); assert!(OptionsBuilder::new().build().is_ok()); assert!(OptionsBuilder::default().build().is_ok()); assert!(OptionsBuilder::default().is_valid()); assert_eq!(options.rebuild(), Options::builder()); } lexical-parse-integer-1.0.5/tests/partial_tests.rs000064400000000000000000000024221046102023000204010ustar 00000000000000use lexical_parse_integer::{FromLexical, FromLexicalWithOptions, Options}; use lexical_util::error::Error; #[cfg(all(feature = "format", feature = "power-of-two"))] use lexical_util::format::NumberFormatBuilder; use lexical_util::format::STANDARD; #[test] fn u8_decimal_test() { assert_eq!(Ok((0, 1)), u8::from_lexical_partial(b"0")); assert_eq!(Ok((127, 3)), u8::from_lexical_partial(b"127")); assert_eq!(Ok((128, 3)), u8::from_lexical_partial(b"128")); assert_eq!(Ok((255, 3)), u8::from_lexical_partial(b"255")); assert_eq!(Err(Error::InvalidDigit(0)), u8::from_lexical(b"-1")); assert_eq!(Ok((1, 1)), u8::from_lexical_partial(b"1a")); let options = Options::default(); assert_eq!(Ok((0, 1)), u8::from_lexical_partial_with_options::<{ STANDARD }>(b"0", &options)); } #[test] #[cfg(all(feature = "format", feature = "power-of-two"))] fn u8_decimal_format_test() { // Test an invalid format. const FORMAT: u128 = NumberFormatBuilder::from_radix(1); let options = Options::default(); assert_eq!( Err(Error::InvalidMantissaRadix), u8::from_lexical_with_options::(b"0", &options) ); assert_eq!( Err(Error::InvalidMantissaRadix), u8::from_lexical_partial_with_options::(b"0", &options) ); } lexical-parse-integer-1.0.5/tests/util.rs000064400000000000000000000034251046102023000165040ustar 00000000000000#![allow(dead_code, unused_imports)] #[cfg(feature = "power-of-two")] use lexical_util::format::NumberFormatBuilder; use proptest::prelude::*; pub(crate) use quickcheck::QuickCheck; #[cfg(feature = "power-of-two")] pub const fn from_radix(radix: u8) -> u128 { NumberFormatBuilder::from_radix(radix) } pub fn default_proptest_config() -> ProptestConfig { ProptestConfig { cases: if cfg!(miri) { 10 } else { ProptestConfig::default().cases }, max_shrink_iters: if cfg!(miri) { 10 } else { ProptestConfig::default().max_shrink_iters }, failure_persistence: if cfg!(miri) { None } else { ProptestConfig::default().failure_persistence }, ..ProptestConfig::default() } } // This is almost identical to quickcheck's itself, just to add default // arguments https://docs.rs/quickcheck/1.0.3/src/quickcheck/lib.rs.html#43-67 // The code is unlicensed. #[macro_export] macro_rules! default_quickcheck { (@as_items $($i:item)*) => ($($i)*); { $( $(#[$m:meta])* fn $fn_name:ident($($arg_name:ident : $arg_ty:ty),*) -> $ret:ty { $($code:tt)* } )* } => ( $crate::default_quickcheck! { @as_items $( #[test] $(#[$m])* fn $fn_name() { fn prop($($arg_name: $arg_ty),*) -> $ret { $($code)* } $crate::util::QuickCheck::new() .max_tests(if cfg!(miri) { 10 } else { 10_000 }) .quickcheck(prop as fn($($arg_ty),*) -> $ret); } )* } ) }