lexical-parse-integer-0.8.6/.cargo_vcs_info.json0000644000000001630000000000100152330ustar { "git": { "sha1": "933e2cca11736fc230ba7c3cbbad58bb58a89d13" }, "path_in_vcs": "lexical-parse-integer" }lexical-parse-integer-0.8.6/CODE_OF_CONDUCT.md000064400000000000000000000230620072674642500166550ustar 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](#enforcement), 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-0.8.6/Cargo.toml0000644000000027520000000000100132370ustar # 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 = "2018" name = "lexical-parse-integer" version = "0.8.6" authors = ["Alex Huszagh "] exclude = [ "assets/*", "docs/*", "etc/*", "cargo-timing*.html", ] 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", ] [dependencies.lexical-util] version = "0.8.5" features = ["parse-integers"] default-features = false [dependencies.static_assertions] version = "1" [dev-dependencies.proptest] version = "0.10.1" [features] compact = ["lexical-util/compact"] default = ["std"] format = ["lexical-util/format"] lint = ["lexical-util/lint"] nightly = [] power-of-two = ["lexical-util/power-of-two"] radix = [ "lexical-util/radix", "power-of-two", ] safe = [] std = ["lexical-util/std"] lexical-parse-integer-0.8.6/Cargo.toml.orig000064400000000000000000000034400072674642500167430ustar 00000000000000[package] authors = ["Alex Huszagh "] autobenches = false categories = ["parsing", "no-std"] description = "Efficient parsing of integers from strings." edition = "2018" 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 = "0.8.6" exclude = [ "assets/*", "docs/*", "etc/*", "cargo-timing*.html" ] [dependencies] static_assertions = "1" [dependencies.lexical-util] version = "0.8.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/neithernut/quickcheck/", branch = "i32min-shrink-bound" } proptest = "0.10.1" [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"] # Ensure only safe indexing is used. This is a no-op, since all # examples of potential memory unsafety are trivial to prove safe. safe = [] # Add support for nightly-only features. nightly = [] # Internal only features. # Enable the lint checks. lint = ["lexical-util/lint"] [package.metadata.docs.rs] features = ["radix", "format"] lexical-parse-integer-0.8.6/LICENSE-APACHE000064400000000000000000000251420072674642500160030ustar 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-0.8.6/LICENSE-MIT000064400000000000000000000017770072674642500155230ustar 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-0.8.6/LICENSE.md000064400000000000000000000454630072674642500154730ustar 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-0.8.6/README.md000064400000000000000000000470760072674642500153500ustar 00000000000000lexical ======= 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. **Similar Projects** If you want a minimal, stable, and compile-time friendly version of lexical's float-parsing algorithm, see [minimal-lexical](https://github.com/Alexhuszagh/minimal-lexical). 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). **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 = "^6.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 = "0.8.5" 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.
- **safe**:   Require all array indexing to be bounds-checked.
This is effectively a no-op for number parsers, since they use safe indexing except where indexing without bounds checking can be trivially shown to be correct. The number writers frequently use unsafe indexing, since we can easily over-estimate the number of digits in the output due to the fixed-length input.
- **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 the safety when bounds checking is disabled, 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 the cost of performance. # Customization > ⚠ **WARNING:** If 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. ## 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. Although lexical may contain bugs leading to rounding error, it is tested against a comprehensive suite of random-data and near-halfway representations, and should be fast and correct for the vast majority of use-cases. # Metrics Various benchmarks, binary sizes, and compile times are shown here: **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** A benchmark on randomly-generated integers uniformly distributed over the entire range. For a more fine-tuned breakdown, see [benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Benchmarks.md). ![Uniform Random Data](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/lexical-parse-integer/assets/random_uniform.svg) **Benchmarks -- Parse Float** A benchmark on parsing floats from various real-world data sets. For a more fine-tuned breakdown, see [benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Benchmarks.md). ![Real Data](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/lexical-parse-float/assets/real.svg) **Benchmarks -- Write Integer** A benchmark on writing random integers uniformly distributed over the entire range. For a more fine-tuned breakdown, see [benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Benchmarks.md). ![Uniform Random Data](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/lexical-write-integer/assets/random_uniform.svg) **Benchmarks -- Write Float** A benchmark on writing floats generated via a random-number generator and parsed from a JSON document. For a more fine-tuned breakdown, see [benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Benchmarks.md). ![Random Data](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/lexical-write-float/assets/json.svg) # Safety Due to the use of memory unsafe code in the integer and float writers, 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. Due to the simple logic of the integer writers, and the lack of memory safety in the integer parsers, we minimally fuzz both, and test it with edge-cases, which has shown no memory safety issues to date. # 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. - mips (MIPS) Linux. - mipsel (MIPS LE) Linux. - mips64 (MIPS64 BE) Linux. - mips64el (MIPS64 LE) Linux. - 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: - v0.8.x - v0.7.x (Maintenance) - v0.6.x (Maintenance) **Rustc Compatibility** - v0.8.x supports 1.51+, including stable, beta, and nightly. - v0.7.x supports 1.37+, including stable, beta, and nightly. - v0.6.x supports Rustc 1.24+, 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-0.8.6/src/algorithm.rs000064400000000000000000000257450072674642500172130ustar 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. #![cfg(not(feature = "compact"))] #![doc(hidden)] use crate::shared::is_overflow; use lexical_util::digit::char_to_digit_const; use lexical_util::format::NumberFormat; use lexical_util::iterator::{AsBytes, BytesIter}; use lexical_util::num::{as_cast, Integer, UnsignedInteger}; use lexical_util::result::Result; use lexical_util::step::min_step; // ALGORITHM /// Check if we can try to parse 8 digits. macro_rules! can_try_parse_multidigits { ($iter:expr, $radix:expr) => { $iter.is_contiguous() && (cfg!(not(feature = "power-of-two")) || $radix <= 10) }; } /// Parse 8-digits at a time. /// /// See the algorithm description in `parse_8digits`. /// This reduces the number of required multiplications /// from 8 to 4. #[rustfmt::skip] macro_rules! parse_8digits { ( $value:ident, $iter:ident, $format:ident, $t:ident ) => {{ let radix: $t = as_cast(NumberFormat::<{ $format }>::MANTISSA_RADIX); let radix2: $t = radix.wrapping_mul(radix); let radix4: $t = radix2.wrapping_mul(radix2); let radix8: $t = radix4.wrapping_mul(radix4); // Try our fast, 8-digit at a time optimizations. while let Some(val8) = try_parse_8digits::<$t, _, $format>(&mut $iter) { $value = $value.wrapping_mul(radix8); $value = $value.wrapping_add(val8); } }}; } /// Parse 4-digits at a time. /// /// See the algorithm description in `parse_4digits`. /// This reduces the number of required multiplications /// from 4 to 3. #[rustfmt::skip] macro_rules! parse_4digits { ( $value:ident, $iter:ident, $format:ident, $t:ident ) => {{ let radix: $t = as_cast(NumberFormat::<{ $format }>::MANTISSA_RADIX); let radix2: $t = radix.wrapping_mul(radix); let radix4: $t = radix2.wrapping_mul(radix2); // Try our fast, 4-digit at a time optimizations. while let Some(val4) = try_parse_4digits::<$t, _, $format>(&mut $iter) { $value = $value.wrapping_mul(radix4); $value = $value.wrapping_add(val4); } }}; } /// Parse digits for a positive or negative value. /// Optimized for operations with machine integers. #[rustfmt::skip] macro_rules! parse_digits { ( $value:ident, $iter:ident, $format:ident, $is_negative:ident, $start_index:ident, $t:ident, $u:ident, $invalid_digit:ident ) => {{ // WARNING: // Performance is heavily dependent on the amount of branching. // We therefore optimize for worst cases only to a certain extent: // that is, since most integers aren't randomly distributed, but // are more likely to be smaller values, we need to avoid overbranching // to ensure small digit parsing isn't impacted too much. We therefore // only enable 4-digit **or** 8-digit optimizations, but not both. // If not, the two branch passes kill performance for small 64-bit // and 128-bit values. // // However, for signed integers, the increased amount of branching // makes these multi-digit optimizations not worthwhile. For large // 64-bit, signed integers, the performance benefit is ~23% faster. // However, the performance penalty for smaller, more common integers // is ~50%. Therefore, these optimizations are not worth the penalty. // // For unsigned and 128-bit signed integers, the performance penalties // are minimal and the performance gains are substantial, so re-enable // the optimizations. // // DO NOT MAKE CHANGES without monitoring the resulting benchmarks, // or performance could greatly be impacted. let radix = NumberFormat::<{ $format }>::MANTISSA_RADIX; // Optimizations for reading 8-digits at a time. // Makes no sense to do 8 digits at a time for 32-bit values, // since it can only hold 8 digits for base 10. if <$t>::BITS == 128 && can_try_parse_multidigits!($iter, radix) { parse_8digits!($value, $iter, $format, $u); } if <$t>::BITS == 64 && can_try_parse_multidigits!($iter, radix) && !<$t>::IS_SIGNED { parse_8digits!($value, $iter, $format, $u); } // Optimizations for reading 4-digits at a time. // 36^4 is larger than a 16-bit integer. Likewise, 10^4 is almost // the limit of u16, so it's not worth it. if <$t>::BITS == 32 && can_try_parse_multidigits!($iter, radix) && !<$t>::IS_SIGNED { parse_4digits!($value, $iter, $format, $u); } parse_1digit!($value, $iter, $format, $is_negative, $start_index, $t, $u, $invalid_digit) }}; } /// Algorithm for the complete parser. #[inline] pub fn algorithm_complete(bytes: &[u8]) -> Result where T: Integer, Unsigned: UnsignedInteger, { algorithm!(bytes, FORMAT, T, Unsigned, parse_digits, invalid_digit_complete, into_ok_complete) } /// Algorithm for the partial parser. #[inline] pub fn algorithm_partial(bytes: &[u8]) -> Result<(T, usize)> where T: Integer, Unsigned: UnsignedInteger, { algorithm!(bytes, FORMAT, T, Unsigned, parse_digits, invalid_digit_partial, into_ok_partial) } // DIGIT OPTIMIZATIONS /// Determine if 4 bytes, read raw from bytes, are 4 digits for the radix. #[inline] 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. #[inline] 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: /// #[inline] pub fn try_parse_4digits<'a, T, Iter, const FORMAT: u128>(iter: &mut Iter) -> Option where T: Integer, Iter: BytesIter<'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.read::()?); 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 } } /// Determine if 8 bytes, read raw from bytes, are 8 digits for the radix. /// See `is_4digits` for the algorithm description. #[inline] 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. #[inline] 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. #[inline] pub fn try_parse_8digits<'a, T, Iter, const FORMAT: u128>(iter: &mut Iter) -> Option where T: Integer, Iter: BytesIter<'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.read::()?); 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 } } lexical-parse-integer-0.8.6/src/api.rs000064400000000000000000000051430072674642500157640ustar 00000000000000////! Implements the algorithm in terms of the lexical API. #![doc(hidden)] use crate::options::Options; use crate::parse::ParseInteger; use lexical_util::format::{NumberFormat, STANDARD}; use lexical_util::{from_lexical, from_lexical_with_options}; /// Implement FromLexical for numeric type. /// /// Need to inline these, otherwise codegen is suboptimal. /// 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 $(, #[$meta:meta])? ; )*) => ($( impl FromLexical for $t { $(#[$meta:meta])? #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical(bytes: &[u8]) -> lexical_util::result::Result { Self::parse_complete::<$unsigned, STANDARD>(bytes) } $(#[$meta:meta])? #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical_partial( bytes: &[u8], ) -> lexical_util::result::Result<(Self, usize)> { Self::parse_partial::<$unsigned, STANDARD>(bytes) } } impl FromLexicalWithOptions for $t { type Options = Options; $(#[$meta:meta])? #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical_with_options( bytes: &[u8], _: &Self::Options, ) -> lexical_util::result::Result { let format = NumberFormat::<{ FORMAT }> {}; if !format.is_valid() { return Err(format.error()); } Self::parse_complete::<$unsigned, FORMAT>(bytes) } $(#[$meta:meta])? #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical_partial_with_options( bytes: &[u8], _: &Self::Options, ) -> lexical_util::result::Result<(Self, usize)> { let format = NumberFormat::<{ FORMAT }> {}; if !format.is_valid() { return Err(format.error()); } Self::parse_partial::<$unsigned, FORMAT>(bytes) } } )*) } 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-0.8.6/src/compact.rs000064400000000000000000000020350072674642500166360ustar 00000000000000//! Radix-generic, unoptimized, string-to-integer conversion routines. //! //! These routines aim to be compact, at the cost of performance. #![cfg(feature = "compact")] #![doc(hidden)] use crate::shared::is_overflow; use lexical_util::digit::char_to_digit_const; use lexical_util::format::NumberFormat; use lexical_util::iterator::{AsBytes, BytesIter}; use lexical_util::num::{as_cast, Integer, UnsignedInteger}; use lexical_util::result::Result; use lexical_util::step::min_step; /// Algorithm for the complete parser. pub fn algorithm_complete(bytes: &[u8]) -> Result where T: Integer, Unsigned: UnsignedInteger, { algorithm!(bytes, FORMAT, T, Unsigned, parse_1digit, invalid_digit_complete, into_ok_complete) } /// Algorithm for the partial parser. pub fn algorithm_partial(bytes: &[u8]) -> Result<(T, usize)> where T: Integer, Unsigned: UnsignedInteger, { algorithm!(bytes, FORMAT, T, Unsigned, parse_1digit, invalid_digit_partial, into_ok_partial) } lexical-parse-integer-0.8.6/src/lib.rs000064400000000000000000000066310072674642500157640ustar 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.51.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) // 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)] #[macro_use] mod shared; pub mod algorithm; pub mod compact; pub mod options; pub mod parse; mod api; // Re-exports pub use self::api::{FromLexical, FromLexicalWithOptions}; #[doc(inline)] pub use self::options::{Options, OptionsBuilder}; 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; lexical-parse-integer-0.8.6/src/options.rs000064400000000000000000000046720072674642500167140ustar 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 {} impl OptionsBuilder { /// Create new options builder with default options. #[inline(always)] pub const fn new() -> Self { 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. /// /// # Safety /// /// Safe as long as`is_valid` is true. #[inline(always)] pub const unsafe fn build_unchecked(&self) -> Options { Options {} } /// Build the Options struct. #[inline(always)] pub const fn build(&self) -> Result { // SAFETY: always safe, since it must be valid. Ok(unsafe { 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 {} impl Options { /// Create options with default values. #[inline(always)] pub const fn new() -> Self { Self {} } /// Check if the options state is valid. #[inline(always)] pub const fn is_valid(&self) -> bool { true } // 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 {} } } 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()); lexical-parse-integer-0.8.6/src/parse.rs000064400000000000000000000023770072674642500163330ustar 00000000000000//! Shared trait and methods for parsing integers. #![doc(hidden)] // Select the correct back-end. #[cfg(not(feature = "compact"))] use crate::algorithm::{algorithm_complete, algorithm_partial}; #[cfg(feature = "compact")] use crate::compact::{algorithm_complete, algorithm_partial}; use lexical_util::num::{Integer, UnsignedInteger}; use lexical_util::result::Result; /// 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]) -> Result { algorithm_complete::<_, Unsigned, { FORMAT }>(bytes) } /// Forward partial parser parameters to the backend. #[cfg_attr(not(feature = "compact"), inline(always))] fn parse_partial( bytes: &[u8], ) -> Result<(Self, usize)> { algorithm_partial::<_, Unsigned, { FORMAT }>(bytes) } } 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-0.8.6/src/shared.rs000064400000000000000000000364010072674642500164620ustar 00000000000000//! Shared algorithms and utilities for parsing integers. //! //! 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::format::NumberFormat; use lexical_util::num::{as_cast, Integer, UnsignedInteger}; use lexical_util::step::max_step; /// Return an error, returning the index and the error. macro_rules! into_error { ($code:ident, $index:expr) => { Err((lexical_util::error::Error::$code($index))) }; } /// Return an value for a complete parser. macro_rules! into_ok_complete { ($value:expr, $index:expr) => { Ok(as_cast($value)) }; } /// Return an value and index for a partial parser. macro_rules! into_ok_partial { ($value:expr, $index:expr) => { Ok((as_cast($value), $index)) }; } /// Return an error for a complete parser upon an invalid digit. macro_rules! invalid_digit_complete { ( $value:ident, $iter:ident, $format:ident, $is_negative:ident, $start_index:ident, $t:ident, $u:ident ) => {{ // Don't do any overflow checking here: we don't need it. into_error!(InvalidDigit, $iter.cursor() - 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:ident, $iter:ident, $format:ident, $is_negative:ident, $start_index:ident, $t:ident, $u:ident ) => {{ let radix = NumberFormat::<{ $format }>::MANTISSA_RADIX; let count = $iter.current_count() - $start_index - 1; if is_overflow::<$t, $u, $format>($value, count, $is_negative) { let min = min_step(radix, <$t as Integer>::BITS, <$t>::IS_SIGNED); if <$t>::IS_SIGNED && $is_negative { into_error!(Underflow, (count - 1).min(min + 1)) } else { into_error!(Overflow, (count - 1).min(min + 1)) } } else if <$t>::IS_SIGNED && $is_negative { into_ok_partial!($value.wrapping_neg(), $iter.cursor() - 1) } else { into_ok_partial!($value, $iter.cursor() - 1) } }}; } /// 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_rules! parse_sign { ($iter:ident, $format:ident) => { // This works in all cases, and gets a few handy // optimizations: // 1. It minimizes branching: we either need to subslice // or return an offset from the loop. We can't increment // the iterator in the loop or it decimates performance. // // Using `iter.peek()` means we respect digit separators at // the start of the number, when they're valid. // // All the other cases are removed at compile time. // Note: the compiler isn't smart enough to realize that // `Some(_) if !$format.call() =>` and `Some(_) =>` are // mutually exclusive, so make sure we manually expand // these cases. match $iter.peek() { Some(&b'+') if !$format.no_positive_mantissa_sign() => (false, 1), Some(&b'+') if $format.no_positive_mantissa_sign() => { return into_error!(InvalidPositiveSign, 0); }, // Don't add the check for the negative sign here if unsigned, // since it absolutely decimates performance. If it's for a // partial parser, we'll simply get 0 digits parsed, like before. // Complete parsers will still error, like before. That is, it's // correct **enough**. Some(&b'-') if T::IS_SIGNED => (true, 1), Some(_) if $format.required_mantissa_sign() => return into_error!(MissingSign, 0), _ => (false, 0), } }; } /// Determine if the value has overflowed. #[cfg_attr(not(feature = "compact"), inline)] pub(super) fn is_overflow( value: U, count: usize, is_negative: bool, ) -> bool where T: Integer, U: UnsignedInteger, { let format = NumberFormat::<{ FORMAT }> {}; let max = max_step(format.radix(), T::BITS, T::IS_SIGNED); let radix: U = as_cast(format.radix()); let min_value: U = radix.pow(max as u32 - 1); if T::IS_SIGNED { // Signed type: have to deal with 2's complement. let max_value: U = as_cast::(T::MAX) + U::ONE; if count > max || (count == max && (value < min_value || value > max_value || (!is_negative && value == max_value))) { // Must have overflowed, or wrapped. // 1. Guaranteed overflow due to too many digits. // 2. Guaranteed overflow due to wrap. // 3. Guaranteed overflow since it's too large for the signed type. // 4. Guaranteed overflow due to 2's complement. return true; } } else if count > max || (count == max && value < min_value) { // Must have overflowed: too many digits or wrapped. return true; } false } /// Parse the value for the given type. macro_rules! parse_value { ( $iter:ident, $is_negative:ident, $format:ident, $start_index:ident, $t:ident, $u:ident, $parser:ident, $invalid_digit:ident, $into_ok:ident ) => {{ // Use a simple optimization: parse as an unsigned integer, using // unsigned arithmetic , avoiding any branching in the initial stage. // We can then validate the input based on the signed integer limits, // and cast the value over, which is fast. Leads to substantial // improvements due to decreased branching for all but `i8`. let mut value = <$u>::ZERO; let format = NumberFormat::<{ $format }> {}; $parser!(value, $iter, $format, $is_negative, $start_index, $t, $u, $invalid_digit); let count = $iter.current_count() - $start_index; if is_overflow::<$t, $u, $format>(value, count, $is_negative) { let min = min_step(format.radix(), <$t as Integer>::BITS, <$t>::IS_SIGNED); if <$t>::IS_SIGNED && $is_negative { into_error!(Underflow, (count - 1).min(min + 1)) } else { into_error!(Overflow, (count - 1).min(min + 1)) } } else if <$t>::IS_SIGNED && $is_negative { // Need to cast it to the signed type first, so we don't // get an invalid representation for i128 if it's widened. $into_ok!(as_cast::<$t, _>(value.wrapping_neg()), $iter.length()) } else { $into_ok!(value, $iter.length()) } }}; } /// Parse a single digit at a time. /// This has no multiple-digit optimizations. #[rustfmt::skip] macro_rules! parse_1digit { ( $value:ident, $iter:ident, $format:ident, $is_negative:ident, $start_index:ident, $t:ident, $u:ident, $invalid_digit:ident ) => {{ let format = NumberFormat::<{ $format }>; let radix = NumberFormat::<{ $format }>::MANTISSA_RADIX; // Do our slow parsing algorithm: 1 digit at a time. while let Some(&c) = $iter.next() { let digit = match char_to_digit_const(c, radix) { Some(v) => v, None => { // 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). let base_suffix = format.base_suffix(); if cfg!(feature = "format") && base_suffix != 0 && $iter.cursor() - $start_index > 1 { let is_suffix = if format.case_sensitive_base_suffix() { c == base_suffix } else { c.to_ascii_lowercase() == base_suffix.to_ascii_lowercase() }; if is_suffix && $iter.is_done() { // Break out of the loop, we've finished parsing. break; } else if is_suffix { // 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_done()` above. unsafe { $iter.step_unchecked() }; } } // Might have handled our base-prefix here. return $invalid_digit!( $value, $iter, $format, $is_negative, $start_index, $t, $u ); }, }; $value = $value.wrapping_mul(as_cast(radix)); $value = $value.wrapping_add(as_cast(digit)); } }}; } /// Generic algorithm for both partial and complete parsers. /// /// * `invalid_digit` - Behavior on finding an invalid digit. /// * `into_ok` - Behavior when returning a valid value. #[rustfmt::skip] macro_rules! algorithm { ( $bytes:ident, $format:ident, $t:ident, $u:ident, $parser:ident, $invalid_digit:ident, $into_ok:ident ) => {{ let format = NumberFormat::<{ $format }> {}; // 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 noskip iterator. let mut byte = $bytes.bytes::<{ $format }>(); let mut iter = byte.integer_iter(); let (is_negative, shift) = parse_sign!(iter, format); // SAFETY: safe since we shift at most one for a parsed sign byte. unsafe { iter.step_by_unchecked(shift) }; if iter.is_done() { return into_error!(Empty, shift); } // Skip any leading zeros. let mut start_index = iter.cursor(); let zeros = iter.skip_zeros(); start_index += zeros; // Now, check to see if we have a valid base prefix. let base_prefix = format.base_prefix(); let mut is_prefix = false; if cfg!(feature = "format") && 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 let Some(&c) = iter.peek() { is_prefix = if format.case_sensitive_base_prefix() { c == base_prefix } else { c.to_ascii_lowercase() == base_prefix.to_ascii_lowercase() }; if is_prefix { // SAFETY: safe since we `byte.len() >= 1`. unsafe { iter.step_unchecked() }; if iter.is_done() { return 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 cfg!(feature = "format") && !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 { return into_error!(InvalidLeadingZeros, index); } match iter.peek().map(|&c| char_to_digit_const(c, format.radix())) { // Valid digit, we have an invalid value. Some(Some(_)) => return into_error!(InvalidLeadingZeros, index), // Either not a digit that follows, or nothing follows. _ => return $into_ok!(<$t>::ZERO, index), }; } // 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. parse_value!( iter, is_negative, $format, start_index, $t, $u, $parser, $invalid_digit, $into_ok ) }}; } lexical-parse-integer-0.8.6/tests/algorithm_tests.rs000064400000000000000000000166630072674642500210070ustar 00000000000000#![cfg(not(feature = "compact"))] #[cfg(feature = "power-of-two")] mod util; use lexical_parse_integer::algorithm; use lexical_util::format::STANDARD; use lexical_util::iterator::AsBytes; use proptest::prelude::*; #[cfg(feature = "power-of-two")] use util::from_radix; #[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) }; } #[test] fn algorithm_test() { let parse_u32 = |bytes: &[u8]| algorithm::algorithm_partial::(bytes); let parse_i32 = |bytes: &[u8]| algorithm::algorithm_partial::(bytes); 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); let parse_i128 = |bytes: &[u8]| algorithm::algorithm_partial::(bytes); 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! { #[test] #[cfg_attr(miri, ignore)] 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] #[cfg_attr(miri, ignore)] 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-0.8.6/tests/api_tests.rs000064400000000000000000000645520072674642500175720ustar 00000000000000#[cfg(feature = "power-of-two")] mod 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; #[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_invalid_digit()); 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 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 $(| $prest:pat)*) => {{ 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! { #[test] #[cfg_attr(miri, ignore)] #[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] #[cfg_attr(miri, ignore)] fn u8_invalid_proptest(i in r"[+]?[0-9]{2}\D") { is_invalid_digit_match!(u8::from_lexical(i.as_bytes()), 2 | 3); } #[test] #[cfg_attr(miri, ignore)] fn u8_overflow_proptest(i in r"[+]?[1-9][0-9]{3}") { is_overflow!(u8::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn u8_negative_proptest(i in r"[-][1-9][0-9]{2}") { is_invalid_digit!(u8::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn u8_double_sign_proptest(i in r"[+]{2}[0-9]{2}") { is_invalid_digit_match!(u8::from_lexical(i.as_bytes()), 1); } #[test] #[cfg_attr(miri, ignore)] fn u8_sign_only_proptest(i in r"[+]") { is_empty!(u8::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] 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] #[cfg_attr(miri, ignore)] fn i8_invalid_proptest(i in r"[+-]?[0-9]{2}\D") { is_invalid_digit_match!(i8::from_lexical(i.as_bytes()), 2 | 3); } #[test] #[cfg_attr(miri, ignore)] fn i8_overflow_proptest(i in r"[+]?[1-9][0-9]{3}") { is_overflow!(i8::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn i8_underflow_proptest(i in r"[-][1-9][0-9]{3}") { is_underflow!(i8::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn i8_double_sign_proptest(i in r"[+-]{2}[0-9]{2}") { is_invalid_digit_match!(i8::from_lexical(i.as_bytes()), 1); } #[test] #[cfg_attr(miri, ignore)] fn i8_sign_only_proptest(i in r"[+-]") { is_empty!(i8::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] 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] #[cfg_attr(miri, ignore)] fn u16_invalid_proptest(i in r"[+]?[0-9]{4}\D") { is_invalid_digit_match!(u16::from_lexical(i.as_bytes()), 4 | 5); } #[test] #[cfg_attr(miri, ignore)] fn u16_overflow_proptest(i in r"[+]?[1-9][0-9]{5}") { is_overflow!(u16::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn u16_negative_proptest(i in r"[-][1-9][0-9]{4}") { is_invalid_digit!(u16::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn u16_double_sign_proptest(i in r"[+]{2}[0-9]{4}") { is_invalid_digit_match!(u16::from_lexical(i.as_bytes()), 1); } #[test] #[cfg_attr(miri, ignore)] fn u16_sign_only_proptest(i in r"[+]") { is_empty!(u16::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] 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] #[cfg_attr(miri, ignore)] fn i16_invalid_proptest(i in r"[+-]?[0-9]{4}\D") { is_invalid_digit_match!(i16::from_lexical(i.as_bytes()), 4 | 5); } #[test] #[cfg_attr(miri, ignore)] fn i16_overflow_proptest(i in r"[+]?[1-9][0-9]{5}") { is_overflow!(i16::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn i16_underflow_proptest(i in r"[-][1-9][0-9]{5}") { is_underflow!(i16::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn i16_double_sign_proptest(i in r"[+-]{2}[0-9]{4}") { is_invalid_digit_match!(i16::from_lexical(i.as_bytes()), 1); } #[test] #[cfg_attr(miri, ignore)] fn i16_sign_only_proptest(i in r"[+-]") { is_empty!(i16::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] 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] #[cfg_attr(miri, ignore)] fn u32_invalid_proptest(i in r"[+]?[0-9]{9}\D") { is_invalid_digit_match!(u32::from_lexical(i.as_bytes()), 9 | 10); } #[test] #[cfg_attr(miri, ignore)] fn u32_overflow_proptest(i in r"[+]?[1-9][0-9]{10}") { is_overflow!(u32::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn u32_negative_proptest(i in r"[-][1-9][0-9]{9}") { is_invalid_digit!(u32::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn u32_double_sign_proptest(i in r"[+]{2}[0-9]{9}") { is_invalid_digit_match!(u32::from_lexical(i.as_bytes()), 1); } #[test] #[cfg_attr(miri, ignore)] fn u32_sign_only_proptest(i in r"[+]") { is_empty!(u32::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] 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] #[cfg_attr(miri, ignore)] fn i32_invalid_proptest(i in r"[+-]?[0-9]{9}\D") { is_invalid_digit_match!(i32::from_lexical(i.as_bytes()), 9 | 10); } #[test] #[cfg_attr(miri, ignore)] fn i32_overflow_proptest(i in r"[+]?[1-9][0-9]{10}") { is_overflow!(i32::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn i32_underflow_proptest(i in r"-[1-9][0-9]{10}") { is_underflow!(i32::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn i32_double_sign_proptest(i in r"[+-]{2}[0-9]{9}") { is_invalid_digit_match!(i32::from_lexical(i.as_bytes()), 1); } #[test] #[cfg_attr(miri, ignore)] fn i32_sign_only_proptest(i in r"[+-]") { is_empty!(i32::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] 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] #[cfg_attr(miri, ignore)] fn u64_invalid_proptest(i in r"[+]?[0-9]{19}\D") { is_invalid_digit_match!(u64::from_lexical(i.as_bytes()), 19 | 20); } #[test] #[cfg_attr(miri, ignore)] fn u64_overflow_proptest(i in r"[+]?[1-9][0-9]{21}") { is_overflow!(u64::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn u64_negative_proptest(i in r"[-][1-9][0-9]{21}") { is_invalid_digit!(u64::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn u64_double_sign_proptest(i in r"[+]{2}[0-9]{19}") { is_invalid_digit_match!(u64::from_lexical(i.as_bytes()), 1); } #[test] #[cfg_attr(miri, ignore)] fn u64_sign_only_proptest(i in r"[+]") { is_empty!(u64::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] 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] #[cfg_attr(miri, ignore)] fn i64_invalid_proptest(i in r"[+-]?[0-9]{18}\D") { is_invalid_digit_match!(i64::from_lexical(i.as_bytes()), 18 | 19); } #[test] #[cfg_attr(miri, ignore)] fn i64_overflow_proptest(i in r"[+]?[1-9][0-9]{19}") { is_overflow!(i64::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn i64_underflow_proptest(i in r"-[1-9][0-9]{19}") { is_underflow!(i64::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn i64_double_sign_proptest(i in r"[+-]{2}[0-9]{18}") { is_invalid_digit_match!(i64::from_lexical(i.as_bytes()), 1); } #[test] #[cfg_attr(miri, ignore)] fn i64_sign_only_proptest(i in r"[+-]") { is_empty!(i64::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] 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] #[cfg_attr(miri, ignore)] fn u128_invalid_proptest(i in r"[+]?[0-9]{38}\D") { is_invalid_digit_match!(u128::from_lexical(i.as_bytes()), 38 | 39); } #[test] #[cfg_attr(miri, ignore)] fn u128_overflow_proptest(i in r"[+]?[1-9][0-9]{39}") { is_overflow!(u128::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn u128_negative_proptest(i in r"[-][1-9][0-9]{39}") { is_invalid_digit!(u128::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn u128_double_sign_proptest(i in r"[+]{2}[0-9]{38}") { is_invalid_digit_match!(u128::from_lexical(i.as_bytes()), 1); } #[test] #[cfg_attr(miri, ignore)] fn u128_sign_only_proptest(i in r"[+]") { is_empty!(u128::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] 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] #[cfg_attr(miri, ignore)] fn i128_invalid_proptest(i in r"[+-]?[0-9]{38}\D") { is_invalid_digit_match!(i128::from_lexical(i.as_bytes()), 38 | 39); } #[test] #[cfg_attr(miri, ignore)] fn i128_overflow_proptest(i in r"[+]?[1-9][0-9]{39}") { is_overflow!(i128::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn i128_underflow_proptest(i in r"-[1-9][0-9]{39}") { is_underflow!(i128::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] fn i128_double_sign_proptest(i in r"[+-]{2}[0-9]{38}") { is_invalid_digit_match!(i128::from_lexical(i.as_bytes()), 1); } #[test] #[cfg_attr(miri, ignore)] fn i128_sign_only_proptest(i in r"[+-]") { is_empty!(i128::from_lexical(i.as_bytes())); } #[test] #[cfg_attr(miri, ignore)] 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-0.8.6/tests/compact_tests.rs000064400000000000000000000013670072674642500204420ustar 00000000000000#![cfg(feature = "compact")] use lexical_parse_integer::compact; use lexical_util::format::STANDARD; #[test] fn algorithm_test() { let parse_u32 = |digits: &[u8]| compact::algorithm_partial::(digits); let parse_i32 = |digits: &[u8]| compact::algorithm_partial::(digits); assert_eq!(parse_u32(b"12345"), Ok((12345, 5))); assert_eq!(parse_u32(b"+12345"), Ok((12345, 6))); // This just parses 0 digits, since it's an unsigned type. 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))); } lexical-parse-integer-0.8.6/tests/options_tests.rs000064400000000000000000000011520072674642500204770ustar 00000000000000use lexical_parse_integer::options::{Options, OptionsBuilder}; #[test] fn options_tests() { let builder = OptionsBuilder::new(); assert!(builder.is_valid()); assert!(unsafe { 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-0.8.6/tests/partial_tests.rs000064400000000000000000000024220072674642500204410ustar 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-0.8.6/tests/util.rs000064400000000000000000000003140072674642500165360ustar 00000000000000#[cfg(feature = "power-of-two")] use lexical_util::format::NumberFormatBuilder; #[cfg(feature = "power-of-two")] pub const fn from_radix(radix: u8) -> u128 { NumberFormatBuilder::from_radix(radix) }