lexical-core-1.0.5/.cargo_vcs_info.json0000644000000001520000000000100134040ustar { "git": { "sha1": "84c1c497edd80ef1f8256be58c44efcfe266fcb8" }, "path_in_vcs": "lexical-core" }lexical-core-1.0.5/CODE_OF_CONDUCT.md000064400000000000000000000230251046102023000147770ustar 00000000000000# Code of Conduct ## When Something Happens If you see a Code of Conduct violation, follow these steps: 1. Let the person know that what they did is not appropriate and ask them to stop and/or edit their message(s) or commits. 2. That person should immediately stop the behavior and correct the issue. 3. If this doesn’t happen, or if you're uncomfortable speaking up, [contact the maintainers](#contacting-maintainers). 4. As soon as available, a maintainer will look into the issue, and take [further action (see below)](#further-enforcement), starting with a warning, then temporary block, then long-term repo or organization ban. When reporting, please include any relevant details, links, screenshots, context, or other information that may be used to better understand and resolve the situation. **The maintainer team will prioritize the well-being and comfort of the recipients of the violation over the comfort of the violator.** See [some examples below](#enforcement-examples). ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers of this project pledge to making participation in our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, technical preferences, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language. - Being respectful of differing viewpoints and experiences. - Gracefully accepting constructive feedback. - Focusing on what is best for the community. - Showing empathy and kindness towards other community members. - Encouraging and raising up your peers in the project so you can all bask in hacks and glory. Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery and unwelcome sexual attention or advances, including when simulated online. The only exception to sexual topics is channels/spaces specifically for topics of sexual identity. - Casual mention of slavery or indentured servitude and/or false comparisons of one's occupation or situation to slavery. Please consider using or asking about alternate terminology when referring to such metaphors in technology. - Making light of/making mocking comments about trigger warnings and content warnings. - Trolling, insulting/derogatory comments, and personal or political attacks. - Public or private harassment, deliberate intimidation, or threats. - Publishing others' private information, such as a physical or electronic address, without explicit permission. This includes any sort of "outing" of any aspect of someone's identity without their consent. - Publishing private screenshots or quotes of interactions in the context of this project without all quoted users' *explicit* consent. - Publishing of private communication that doesn't have to do with reporting harrassment. - Any of the above even when [presented as "ironic" or "joking"](https://en.wikipedia.org/wiki/Hipster_racism). - Any attempt to present "reverse-ism" versions of the above as violations. Examples of reverse-isms are "reverse racism", "reverse sexism", "heterophobia", and "cisphobia". - Unsolicited explanations under the assumption that someone doesn't already know it. Ask before you teach! Don't assume what people's knowledge gaps are. - [Feigning or exaggerating surprise](https://www.recurse.com/manual#no-feigned-surprise) when someone admits to not knowing something. - "[Well-actuallies](https://www.recurse.com/manual#no-well-actuallys)" - Other conduct which could reasonably be considered inappropriate in a professional or community setting. ## Scope This Code of Conduct applies both within spaces involving this project and in other spaces involving community members. This includes the repository, its Pull Requests and Issue tracker, private email communications in the context of the project, and any events where members of the project are participating, as well as adjacent communities and venues affecting the project's members. Depending on the violation, the maintainers may decide that violations of this code of conduct that have happened outside of the scope of the community may deem an individual unwelcome, and take appropriate action to maintain the comfort and safety of its members. ### Other Community Standards As a project on GitHub, this project is additionally covered by the [GitHub Community Guidelines](https://help.github.com/articles/github-community-guidelines/). Enforcement of those guidelines after violations overlapping with the above are the responsibility of the entities, and enforcement may happen in any or all of the services/communities. ## Maintainer Enforcement Process Once the maintainers get involved, they will follow a documented series of steps and do their best to preserve the well-being of project members. This section covers actual concrete steps. ### Contacting Maintainers You may get in touch with the maintainer team through any of the following methods: Through email: ahuszagh@gmail.com (Alex Huszagh) ### Further Enforcement If you've already followed the [initial enforcement steps](#maintainer-enforcement-process), these are the steps maintainers will take for further enforcement, as needed: 1. Repeat the request to stop. 2. If the person doubles down, they will have offending messages removed or edited by a maintainers given an official warning. The PR or Issue may be locked. 3. If the behavior continues or is repeated later, the person will be blocked from participating for 24 hours. 4. If the behavior continues or is repeated after the temporary block, a long-term (6-12mo) ban will be used. On top of this, maintainers may remove any offending messages, images, contributions, etc, as they deem necessary. Maintainers reserve full rights to skip any of these steps, at their discretion, if the violation is considered to be a serious and/or immediate threat to the health and well-being of members of the community. These include any threats, serious physical or verbal attacks, and other such behavior that would be completely unacceptable in any social setting that puts our members at risk. Members expelled from events or venues with any sort of paid attendance will not be refunded. ### Who Watches the Watchers? Maintainers and other leaders who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. These may include anything from removal from the maintainer team to a permanent ban from the community. Additionally, as a project hosted on both GitHub and npm, [their own Codes of Conducts may be applied against maintainers of this project](#other-community-standards), externally of this project's procedures. ### Enforcement Examples #### The Best Case The vast majority of situations work out like this. This interaction is common, and generally positive. > Alex: "Yeah I used X and it was really crazy!" > Patt (not a maintainer): "Hey, could you not use that word? What about 'ridiculous' instead?" > Alex: "oh sorry, sure." -> edits old comment to say "it was really confusing!" #### The Maintainer Case Sometimes, though, you need to get maintainers involved. Maintainers will do their best to resolve conflicts, but people who were harmed by something **will take priority**. > Patt: "Honestly, sometimes I just really hate using $library and anyone who uses it probably sucks at their job." > Alex: "Whoa there, could you dial it back a bit? There's a CoC thing about attacking folks' tech use like that." > Patt: "I'm not attacking anyone, what's your problem?" > Alex: "@maintainers hey uh. Can someone look at this issue? Patt is getting a bit aggro. I tried to nudge them about it, but nope." > KeeperOfCommitBits: (on issue) "Hey Patt, maintainer here. Could you tone it down? This sort of attack is really not okay in this space." > Patt: "Leave me alone I haven't said anything bad wtf is wrong with you." > KeeperOfCommitBits: (deletes user's comment), "@patt I mean it. Please refer to the CoC over at (URL to this CoC) if you have questions, but you can consider this an actual warning. I'd appreciate it if you reworded your messages in this thread, since they made folks there uncomfortable. Let's try and be kind, yeah?" > Patt: "@keeperofbits Okay sorry. I'm just frustrated and I'm kinda burnt out and I guess I got carried away. I'll DM Alex a note apologizing and edit my messages. Sorry for the trouble." > KeeperOfCommitBits: "@patt Thanks for that. I hear you on the stress. Burnout sucks :/. Have a good one!" #### The Nope Case > PepeTheFrog🐸: "Hi, I am a literal actual nazi and I think white supremacists are quite fashionable." > Patt: "NOOOOPE. OH NOPE NOPE." > Alex: "JFC NO. NOPE. @keeperofbits NOPE NOPE LOOK HERE" > KeeperOfCommitBits: "👀 Nope. NOPE NOPE NOPE. 🔥" > PepeTheFrog🐸 has been banned from all organization or user repositories belonging to KeeperOfCommitBits. ## Attribution This Code of Conduct was generated using [WeAllJS Code of Conduct Generator](https://npm.im/weallbehave), which is based on the [WeAllJS Code of Conduct](https://wealljs.org/code-of-conduct), which is itself based on [Contributor Covenant](http://contributor-covenant.org), version 1.4, available at [http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4), and the LGBTQ in Technology Slack [Code of Conduct](http://lgbtq.technology/coc.html). lexical-core-1.0.5/Cargo.toml0000644000000075200000000000100114100ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.63.0" name = "lexical-core" version = "1.0.5" authors = ["Alex Huszagh "] build = false exclude = [ "assets/*", "docs/*", "etc/*", "cargo-timing*.html", ] autobins = false autoexamples = false autotests = false autobenches = false description = "Lexical, to- and from-string conversion routines." readme = "README.md" keywords = [ "parsing", "lexical", "encoding", "no_std", ] categories = [ "parsing", "encoding", "no-std", "value-formatting", ] license = "MIT/Apache-2.0" repository = "https://github.com/Alexhuszagh/rust-lexical" [package.metadata.docs.rs] features = [ "radix", "format", "write-integers", "write-floats", "parse-integers", "parse-floats", ] [lib] name = "lexical_core" path = "src/lib.rs" [[test]] name = "api_tests" path = "tests/api_tests.rs" [[test]] name = "float_pow2_tests" path = "tests/float_pow2_tests.rs" [[test]] name = "float_radix_tests" path = "tests/float_radix_tests.rs" [[test]] name = "issue_97_tests" path = "tests/issue_97_tests.rs" [dependencies.lexical-parse-float] version = "1.0.5" optional = true default-features = false [dependencies.lexical-parse-integer] version = "1.0.5" optional = true default-features = false [dependencies.lexical-util] version = "1.0.5" default-features = false [dependencies.lexical-write-float] version = "1.0.5" optional = true default-features = false [dependencies.lexical-write-integer] version = "1.0.5" optional = true default-features = false [dev-dependencies.approx] version = "0.5.0" [features] compact = [ "lexical-write-integer?/compact", "lexical-write-float?/compact", "lexical-parse-integer?/compact", "lexical-parse-float?/compact", ] default = [ "std", "write-integers", "write-floats", "parse-integers", "parse-floats", ] f128 = [ "lexical-util/f128", "lexical-parse-float?/f128", "lexical-write-float?/f128", ] f16 = [ "lexical-util/f16", "lexical-parse-float?/f16", "lexical-write-float?/f16", ] floats = ["lexical-util/floats"] format = [ "lexical-util/format", "lexical-parse-integer?/format", "lexical-parse-float?/format", "lexical-write-integer?/format", "lexical-write-float?/format", ] integers = ["lexical-util/integers"] lint = [ "lexical-util/lint", "lexical-write-integer?/lint", "lexical-write-float?/lint", "lexical-parse-integer?/lint", "lexical-parse-float?/lint", ] parse = ["lexical-util/parse"] parse-floats = [ "lexical-parse-float", "parse", "floats", ] parse-integers = [ "lexical-parse-integer", "parse", "integers", ] power-of-two = [ "lexical-util/power-of-two", "lexical-write-integer?/power-of-two", "lexical-write-float?/power-of-two", "lexical-parse-integer?/power-of-two", "lexical-parse-float?/power-of-two", ] radix = [ "lexical-util/radix", "lexical-write-integer?/radix", "lexical-write-float?/radix", "lexical-parse-integer?/radix", "lexical-parse-float?/radix", ] std = [ "lexical-util/std", "lexical-write-integer/std", "lexical-write-float/std", "lexical-parse-integer/std", "lexical-parse-float/std", ] write = ["lexical-util/write"] write-floats = [ "lexical-write-float", "write", "floats", ] write-integers = [ "lexical-write-integer", "write", "integers", ] lexical-core-1.0.5/Cargo.toml.orig000064400000000000000000000103121046102023000150620ustar 00000000000000[package] authors = ["Alex Huszagh "] autobenches = false categories = ["parsing", "encoding", "no-std", "value-formatting"] description = "Lexical, to- and from-string conversion routines." edition = "2021" keywords = ["parsing", "lexical", "encoding", "no_std"] license = "MIT/Apache-2.0" name = "lexical-core" readme = "README.md" repository = "https://github.com/Alexhuszagh/rust-lexical" version = "1.0.5" rust-version = "1.63.0" exclude = [ "assets/*", "docs/*", "etc/*", "cargo-timing*.html" ] [dependencies.lexical-util] version = "1.0.5" default-features = false path = "../lexical-util" [dependencies.lexical-parse-integer] version = "1.0.5" optional = true default-features = false path = "../lexical-parse-integer" [dependencies.lexical-parse-float] version = "1.0.5" optional = true default-features = false path = "../lexical-parse-float" [dependencies.lexical-write-integer] version = "1.0.5" optional = true default-features = false path = "../lexical-write-integer" [dependencies.lexical-write-float] version = "1.0.5" optional = true default-features = false path = "../lexical-write-float" [dev-dependencies] approx = "0.5.0" [features] # Need to enable all for backwards compatibility. default = ["std", "write-integers", "write-floats", "parse-integers", "parse-floats"] # Use the standard library. std = [ "lexical-util/std", "lexical-write-integer/std", "lexical-write-float/std", "lexical-parse-integer/std", "lexical-parse-float/std" ] # Add support for writing integers. write-integers = ["lexical-write-integer", "write", "integers"] # Add support for writing floats. write-floats = ["lexical-write-float", "write", "floats"] # Add support for parsing integers. parse-integers = ["lexical-parse-integer", "parse", "integers"] # Add support for parsing floats. parse-floats = ["lexical-parse-float", "parse", "floats"] # Add support for parsing power-of-two float strings. power-of-two = [ "lexical-util/power-of-two", "lexical-write-integer?/power-of-two", "lexical-write-float?/power-of-two", "lexical-parse-integer?/power-of-two", "lexical-parse-float?/power-of-two" ] # Add support for parsing non-decimal float strings. radix = [ "lexical-util/radix", "lexical-write-integer?/radix", "lexical-write-float?/radix", "lexical-parse-integer?/radix", "lexical-parse-float?/radix" ] # Add support for parsing custom numerical formats. format = [ "lexical-util/format", "lexical-parse-integer?/format", "lexical-parse-float?/format", "lexical-write-integer?/format", "lexical-write-float?/format" ] # Reduce code size at the cost of performance. compact = [ "lexical-write-integer?/compact", "lexical-write-float?/compact", "lexical-parse-integer?/compact", "lexical-parse-float?/compact" ] # Enable support for 16-bit floats. f16 = [ "lexical-util/f16", "lexical-parse-float?/f16", "lexical-write-float?/f16" ] # INTERNAL ONLY # ------------- # Internal only features. These are not meant to be used directly. # Enable the lint checks. lint = [ "lexical-util/lint", "lexical-write-integer?/lint", "lexical-write-float?/lint", "lexical-parse-integer?/lint", "lexical-parse-float?/lint" ] # Add support for writing numbers. # Library users should use `write-integers` and `write-floats` instead. write = ["lexical-util/write"] # Add support for parsing numbers. # Library users should use `parse-integers` and `parse-floats` instead. parse = ["lexical-util/parse"] # Add support for conversions to or from integers. # Library users should use `write-integers` and `parse-integers` instead. integers = ["lexical-util/integers"] # Add support for conversions to or from floats. # Library users should use `write-floats` and `parse-floats` instead. floats = ["lexical-util/floats"] # UNSUPPORTED # ----------- # Currently unsupported features. # Enable support for 128-bit floats. Unsupported and unlikely to ever be. # https://github.com/Alexhuszagh/rust-lexical/issues/46 f128 = [ "lexical-util/f128", "lexical-parse-float?/f128", "lexical-write-float?/f128" ] [package.metadata.docs.rs] features = ["radix", "format", "write-integers", "write-floats", "parse-integers", "parse-floats"] lexical-core-1.0.5/LICENSE-APACHE000064400000000000000000000251421046102023000141260ustar 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-core-1.0.5/LICENSE-MIT000064400000000000000000000017771046102023000136460ustar 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-core-1.0.5/LICENSE.md000064400000000000000000000454631046102023000136160ustar 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-core-1.0.5/README.md000064400000000000000000000523741046102023000134700ustar 00000000000000# lexical High-performance numeric conversion routines for use in a `no_std` environment. This does not depend on any standard library features, nor a system allocator. Comprehensive benchmarks can be found at [lexical-benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks). **Similar Projects** If you want a minimal, performant float parser, recent versions of the Rust standard library should be [sufficient](https://github.com/rust-lang/rust/pull/86761). For high-performance integer formatters, look at [itoa](https://docs.rs/itoa/latest/itoa/). The [metrics](#metrics) section contains a detailed comparison of various crates and their performance in comparison to lexical. Lexical is the currently fastest Rust number formatter and parser, and is tested against: - [itoa](https://crates.io/crates/itoa) - [dtoa](https://crates.io/crates/dtoa) - [ryu](https://crates.io/crates/ryu) - Rust core library **Table of Contents** - [Getting Started](#getting-started) - [Partial/Complete Parsers](#partialcomplete-parsers) - [no_std](#no_std) - [Features](#features) - [Customization](#customization) - [Number Format API](#number-format-api) - [Options API](#options-api) - [Documentation](#documentation) - [Validation](#validation) - [Metrics](#metrics) - [Safety](#safety) - [Platform Support](#platform-support) - [Versioning and Version Support](#versioning-and-version-support) - [Changelog](#changelog) - [License](#license) - [Contributing](#contributing) ## Getting Started Add lexical to your `Cargo.toml`: ```toml [dependencies] lexical-core = "^1.0" ``` And get started using lexical: ```rust // Number to string use lexical_core::BUFFER_SIZE; let mut buffer = [b'0'; BUFFER_SIZE]; lexical_core::write(3.0, &mut buffer); // "3.0", always has a fraction suffix, lexical_core::write(3, &mut buffer); // "3" // String to number. let i: i32 = lexical_core::parse("3")?; // Ok(3), auto-type deduction. let f: f32 = lexical_core::parse("3.5")?; // Ok(3.5) let d: f64 = lexical_core::parse("3.5")?; // Ok(3.5), error checking parse. let d: f64 = lexical_core::parse("3a")?; // Err(Error(_)), failed to parse. ``` In order to use lexical in generic code, the trait bounds `FromLexical` (for `parse`) and `ToLexical` (for `to_string`) are provided. ```rust /// Multiply a value in a string by multiplier, and serialize to string. fn mul_2(value: &str, multiplier: T) -> Result where T: lexical_core::ToLexical + lexical_core::FromLexical, { let value: T = lexical_core::parse(value.as_bytes())?; let mut buffer = [b'0'; lexical_core::BUFFER_SIZE]; let bytes = lexical_core::write(value * multiplier, &mut buffer); Ok(std::str::from_utf8(bytes).unwrap()) } ``` ## Partial/Complete Parsers Lexical has both partial and complete parsers: the complete parsers ensure the entire buffer is used while parsing, without ignoring trailing characters, while the partial parsers parse as many characters as possible, returning both the parsed value and the number of parsed digits. Upon encountering an error, lexical will return an error indicating both the error type and the index at which the error occurred inside the buffer. **Complete Parsers** ```rust // This will return Err(Error::InvalidDigit(3)), indicating // the first invalid character occurred at the index 3 in the input // string (the space character). let x: i32 = lexical_core::parse(b"123 456")?; ``` **Partial Parsers** ```rust // This will return Ok((123, 3)), indicating that 3 digits were successfully // parsed, and that the returned value is `123`. let (x, count): (i32, usize) = lexical_core::parse_partial(b"123 456")?; ``` ## no_std `lexical-core` does not depend on a standard library, nor a system allocator. To use `lexical-core` in a `no_std` environment, add the following to `Cargo.toml`: ```toml [dependencies.lexical-core] version = "1.0.0" default-features = false # Can select only desired parsing/writing features. features = ["write-integers", "write-floats", "parse-integers", "parse-floats"] ``` And get started using lexical: ```rust // A constant for the maximum number of bytes a formatter will write. use lexical_core::BUFFER_SIZE; let mut buffer = [b'0'; BUFFER_SIZE]; // Number to string. The underlying buffer must be a slice of bytes. let count = lexical_core::write(3.0, &mut buffer); assert_eq!(buffer[..count], b"3.0"); let count = lexical_core::write(3i32, &mut buffer); assert_eq!(buffer[..count], b"3"); // String to number. The input must be a slice of bytes. let i: i32 = lexical_core::parse(b"3")?; // Ok(3), auto-type deduction. let f: f32 = lexical_core::parse(b"3.5")?; // Ok(3.5) let d: f64 = lexical_core::parse(b"3.5")?; // Ok(3.5), error checking parse. let d: f64 = lexical_core::parse(b"3a")?; // Err(Error(_)), failed to parse. ``` ## Features Lexical feature-gates each numeric conversion routine, resulting in faster compile times if certain numeric conversions. These features can be enabled/disabled for both `lexical-core` (which does not require a system allocator) and `lexical`. By default, all conversions are enabled. - **parse-floats**:   Enable string-to-float conversions. - **parse-integers**:   Enable string-to-integer conversions. - **write-floats**:   Enable float-to-string conversions. - **write-integers**:   Enable integer-to-string conversions. Lexical is highly customizable, and contains numerous other optional features: - **std**:   Enable use of the Rust standard library (enabled by default). - **power-of-two**:   Enable conversions to and from non-decimal strings.
With power_of_two enabled, the radixes {2, 4, 8, 10, 16, and 32} are valid, otherwise, only 10 is valid. This enables common conversions to/from hexadecimal integers/floats, without requiring large pre-computed tables for other radixes.
- **radix**:   Allow conversions to and from non-decimal strings.
With radix enabled, any radix from 2 to 36 (inclusive) is valid, otherwise, only 10 is valid.
- **format**:   Customize acceptable number formats for number parsing and writing.
With format enabled, the number format is dictated through bitflags and masks packed into a u128. These dictate the valid syntax of parsed and written numbers, including enabling digit separators, requiring integer or fraction digits, and toggling case-sensitive exponent characters.
- **compact**:   Optimize for binary size at the expense of performance.
This minimizes the use of pre-computed tables, producing significantly smaller binaries.
- **f16**:   Add support for numeric conversions to-and-from 16-bit floats.
Adds f16, a half-precision IEEE-754 floating-point type, and bf16, the Brain Float 16 type, and numeric conversions to-and-from these floats. Note that since these are storage formats, and therefore do not have native arithmetic operations, all conversions are done using an intermediate f32.
To ensure memory safety, we extensively fuzz the all numeric conversion routines. See the [Safety](#safety) section below for more information. Lexical also places a heavy focus on code bloat: with algorithms both optimized for performance and size. By default, this focuses on performance, however, using the `compact` feature, you can also opt-in to reduced code size at the cost of performance. The compact algorithms minimize the use of pre-computed tables and other optimizations at a major cost to performance. ## Customization Lexical is extensively customizable to support parsing numbers from a wide variety of programming languages, such as `1_2_3`. However, lexical takes the concept of "you don't pay for what you don't use" seriously: enabling the `format` feature does not affect the performance of parsing regular numbers: only those with digit separators. > ⚠ **WARNING:** When changing the number of significant digits written, disabling the use of exponent notation, or changing exponent notation thresholds, `BUFFER_SIZE` may be insufficient to hold the resulting output. `WriteOptions::buffer_size` will provide a correct upper bound on the number of bytes written. If a buffer of insufficient length is provided, lexical-core will panic. Every language has competing specifications for valid numerical input, meaning a number parser for Rust will incorrectly accept or reject input for different programming or data languages. For example: ```rust // Valid in Rust strings. // Not valid in JSON. let f: f64 = lexical_core::parse(b"3.e7")?; // 3e7 // Let's only accept JSON floats. const JSON: u128 = lexical_core::format::JSON; let options = ParseFloatOptions::new(); let f: f64 = lexical_core::parse_with_options::(b"3.0e7", &options)?; // 3e7 let f: f64 = lexical_core::parse_with_options::(b"3.e7", &options)?; // Errors! ``` Due the high variability in the syntax of numbers in different programming and data languages, we provide 2 different APIs to simplify converting numbers with different syntax requirements. - Number Format API (feature-gated via `format` or `power-of-two`).
This is a packed struct contained flags to specify compile-time syntax rules for number parsing or writing. This includes features such as the radix of the numeric string, digit separators, case-sensitive exponent characters, optional base prefixes/suffixes, and more.
- Options API.
This contains run-time rules for parsing and writing numbers. This includes exponent break points, rounding modes, the exponent and decimal point characters, and the string representation of NaN and Infinity.
A limited subset of functionality is documented in examples below, however, the complete specification can be found in the API reference documentation ([parse-float](https://docs.rs/lexical-parse-float/latest/lexical_parse_float/struct.Options.html), [parse-integer](https://docs.rs/lexical-parse-integer/latest/lexical_parse_integer/struct.Options.html), and [write-float](https://docs.rs/lexical-write-float/latest/lexical_write_float/struct.Options.html)). ### Number Format API The number format class provides numerous flags to specify number syntax when parsing or writing. When the `power-of-two` feature is enabled, additional flags are added: - The radix for the significant digits (default `10`). - The radix for the exponent base (default `10`). - The radix for the exponent digits (default `10`). When the `format` feature is enabled, numerous other syntax and digit separator flags are enabled, including: - A digit separator character, to group digits for increased legibility. - Whether leading, trailing, internal, and consecutive digit separators are allowed. - Toggling required float components, such as digits before the decimal point. - Toggling whether special floats are allowed or are case-sensitive. Many pre-defined constants therefore exist to simplify common use-cases, including: - JSON, XML, TOML, YAML, SQLite, and many more. - Rust, Python, C#, FORTRAN, COBOL literals and strings, and many more. An example of building a custom number format is as follows: ```rust const FORMAT: u128 = lexical_core::NumberFormatBuilder::new() // Disable exponent notation. .no_exponent_notation(true) // Disable all special numbers, such as Nan and Inf. .no_special(true) .build(); // Due to use in a `const fn`, we can't panic or expect users to unwrap invalid // formats, so it's up to the caller to verify the format. If an invalid format // is provided to a parser or writer, the function will error or panic, respectively. debug_assert!(lexical_core::format_is_valid::()); ``` ### Options API The options API allows customizing number parsing and writing at run-time, such as specifying the maximum number of significant digits, exponent characters, and more. An example of building a custom options struct is as follows: ```rust use std::num; let options = lexical_core::WriteFloatOptions::builder() // Only write up to 5 significant digits, IE, `1.23456` becomes `1.2345`. .max_significant_digits(num::NonZeroUsize::new(5)) // Never write less than 5 significant digits, `1.1` becomes `1.1000`. .min_significant_digits(num::NonZeroUsize::new(5)) // Trim the trailing `.0` from integral float strings. .trim_floats(true) // Use a European-style decimal point. .decimal_point(b',') // Panic if we try to write NaN as a string. .nan_string(None) // Write infinity as "Infinity". .inf_string(Some(b"Infinity")) .build() .unwrap(); ``` ## Documentation Lexical's API reference can be found on [docs.rs](https://docs.rs/lexical), as can [lexical-core's](lexical-core). Detailed descriptions of the algorithms used can be found here: - [Parsing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Algorithm.md) - [Parsing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Algorithm.md) - [Writing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Algorithm.md) - [Writing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Algorithm.md) In addition, descriptions of how lexical handles [digit separators](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/DigitSeparators.md) and implements [big-integer arithmetic](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/BigInteger.md) are also documented. ## Validation **Float-Parsing** Float parsing is difficult to do correctly, and major bugs have been found in implementations from [libstdc++'s strtod](https://www.exploringbinary.com/glibc-strtod-incorrectly-converts-2-to-the-negative-1075/) to [Python](https://bugs.python.org/issue7632). In order to validate the accuracy of the lexical, we employ the following external tests: 1. Hrvoje Abraham's [strtod](https://github.com/ahrvoje/numerics/tree/master/strtod) test cases. 2. Rust's [test-float-parse](https://github.com/rust-lang/rust/tree/64185f205dcbd8db255ad6674e43c63423f2369a/src/etc/test-float-parse) unittests. 3. Testbase's [stress tests](https://www.icir.org/vern/papers/testbase-report.pdf) for converting from decimal to binary. 4. Nigel Tao's [tests](https://github.com/nigeltao/parse-number-fxx-test-data) extracted from test suites for Freetype, Google's double-conversion library, IBM's IEEE-754R compliance test, as well as numerous other curated examples. 5. [Various](https://www.exploringbinary.com/glibc-strtod-incorrectly-converts-2-to-the-negative-1075/) [difficult](https://www.exploringbinary.com/how-glibc-strtod-works/) [cases](https://www.exploringbinary.com/how-strtod-works-and-sometimes-doesnt/) reported on blogs. Lexical is extensively used in production, the same float parsing algorithm has been adopted by Golang's and Rust's standard libraries, and is unlikely to have correctness issues. ## Metrics Various benchmarks, binary sizes, and compile times are shown here. All the benchmarks can be found on [lexical-benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks?tab=readme-ov-file#latest-results). All benchmarks used a black box to avoid optimizing out the result and leading to misleading metrics. **Build Timings** The compile-times when building with all numeric conversions enabled. For a more fine-tuned breakdown, see [build timings](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BuildTimings.md). ![Build Timings](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/timings_all_posix.svg) **Binary Size** The binary sizes of stripped binaries compiled at optimization level "2". For a more fine-tuned breakdown, see [binary sizes](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BinarySize.md). ![Parse Stripped - Optimization Level "2"](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/size_parse_stripped_opt2_posix.svg) ![Write Stripped - Optimization Level "2"](https://raw.githubusercontent.com/Alexhuszagh/rust-lexical/main/assets/size_write_stripped_opt2_posix.svg) ### Benchmarks — Parse Integer **Random** A benchmark on randomly-generated integers uniformly distributed over the entire range. ![Uniform Random Data](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/json_random%20-%20parse%20int%20-%20core,lexical.png) **Simple** A benchmark on randomly-generated integers from 1-1000. ![Simple Random Data](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/json_simple%20-%20parse%20int%20-%20core,lexical.png) ### Benchmarks — Parse Float **Real-World Datasets** A benchmark on parsing floats from various real-world data sets, including Canada, Mesh, and astronomical data (earth). ![Canada](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/canada%20-%20parse%20float%20-%20core,lexical.png) ![Earth](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/earth%20-%20parse%20float%20-%20core,lexical.png) ![Mesh](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/mesh%20-%20parse%20float%20-%20core,lexical.png) **Random** A benchmark on randomly-generated integers uniformly distributed over the entire range. ![Random Big Integer](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/random_big_ints%20-%20parse%20float%20-%20core,lexical.png) **Simple** A benchmark on randomly-generated integers from 1-1000. ![Random Simple](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/random_simple_int64%20-%20parse%20float%20-%20core,lexical.png) ### Benchmarks — Write Integer **Random** A benchmark on randomly-generated integers uniformly distributed over the entire range. ![Random Uniform](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/json_chain_random%20-%20write%20int%20-%20fmt,itoa,lexical.png) **Simple** ![Random Simple](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/json_simple%20-%20write%20int%20-%20fmt,itoa,lexical.png) **Large** ![Random Large](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/random_large%20-%20write%20int%20-%20fmt,itoa,lexical.png) ### Benchmarks — Write Float **Big Integer** A benchmarks for values with a large integers. ![Big Integers](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/random_big_ints%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png) **Simple 64-Bit Inteers** ![Simple Int64](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/random_simple_int64%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png) **Random** ![Random](https://github.com/Alexhuszagh/lexical-benchmarks/raw/main/results/edceaca/plot/json%20-%20write%20float%20-%20dtoa,fmt,lexical,ryu.png) ## Safety Due to the use of memory unsafe code in the library, we extensively fuzz our float writers and parsers. The fuzz harnesses may be found under [fuzz](https://github.com/Alexhuszagh/rust-lexical/tree/main/fuzz), and are run continuously. So far, we've parsed and written over 72 billion floats. ## Platform Support lexical-core is tested on a wide variety of platforms, including big and small-endian systems, to ensure portable code. Supported architectures include: - x86_64 Linux, Windows, macOS, Android, iOS, FreeBSD, and NetBSD. - x86 Linux, macOS, Android, iOS, and FreeBSD. - aarch64 (ARM8v8-A) Linux, Android, and iOS. - armv7 (ARMv7-A) Linux, Android, and iOS. - arm (ARMv6) Linux, and Android. - powerpc (PowerPC) Linux. - powerpc64 (PPC64) Linux. - powerpc64le (PPC64LE) Linux. - s390x (IBM Z) Linux. lexical-core should also work on a wide variety of other architectures and ISAs. If you have any issue compiling lexical-core on any architecture, please file a bug report. ## Versioning and Version Support **Version Support** The currently supported versions are: - v1.0.x Due to security considerations, all other versions are not supported and security advisories exist for them.. **Rustc Compatibility** - v1.0.x supports 1.63+, including stable, beta, and nightly. Please report any errors compiling a supported lexical-core version on a compatible Rustc version. **Versioning** lexical uses [semantic versioning](https://semver.org/). Removing support for Rustc versions newer than the latest stable Debian or Ubuntu version is considered an incompatible API change, requiring a major version change. ## Changelog All changes are documented in [CHANGELOG](https://github.com/Alexhuszagh/rust-lexical/blob/main/CHANGELOG). ## License Lexical is dual licensed under the Apache 2.0 license as well as the MIT license. See the [LICENSE.md](LICENSE.md) file for full license details. ## Contributing Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in lexical by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. Contributing to the repository means abiding by the [code of conduct](https://github.com/Alexhuszagh/rust-lexical/blob/main/CODE_OF_CONDUCT.md). For the process on how to contribute to lexical, see the [development](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/Development.md) quick-start guide. lexical-core-1.0.5/src/lib.rs000064400000000000000000000701221046102023000141030ustar 00000000000000//! Fast lexical conversion routines for a `no_std` environment. //! //! lexical-core is a low-level API for number-to-string and //! string-to-number conversions, without requiring a system //! allocator. 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. //! //! Despite the low-level API and focus on performance, lexical-core //! strives to be simple and yet configurable: despite supporting nearly //! every float and integer format available, it only exports 4 write //! functions and 4 parse functions. //! //! lexical-core is well-tested, and has been downloaded more than 5 million //! times and currently has no known errors in correctness. lexical-core //! prioritizes performance above all else, and aims to be competitive //! or faster than any other float or integer parser and writer. //! //! In addition, despite having a large number of features, configurability, //! and a focus on performance, we also strive for fast compile times. //! Recent versions also add support for smaller binary sizes, as well //! ideal for embedded or web environments, where executable bloat can //! be much more detrimental than performance. //! //! # Getting Started //! //! ```rust //! # #[cfg(all( //! # feature = "parse-floats", //! # feature = "parse-integers", //! # feature = "write-floats", //! # feature = "write-integers", //! # ))] //! # { //! //! // String to number using Rust slices. //! // The argument is the byte string parsed. //! let f: f32 = lexical_core::parse(b"3.5").unwrap(); // 3.5 //! let i: i32 = lexical_core::parse(b"15").unwrap(); // 15 //! //! // All lexical_core parsers are checked, they validate the //! // input data is entirely correct, and stop parsing when invalid data //! // is found, or upon numerical overflow. //! let r = lexical_core::parse::(b"256"); // Err(ErrorCode::Overflow.into()) //! let r = lexical_core::parse::(b"1a5"); // Err(ErrorCode::InvalidDigit.into()) //! //! // In order to extract and parse a number from a substring of the input //! // data, use `parse_partial`. These functions return the parsed value and //! // the number of processed digits, allowing you to extract and parse the //! // number in a single pass. //! let r = lexical_core::parse_partial::(b"3a5"); // Ok((3, 1)) //! //! // If an insufficiently long buffer is passed, the serializer will panic. //! //! // PANICS //! let mut buf = [b'0'; 1]; //! //let slc = lexical_core::write::(15, &mut buf); //! //! // In order to guarantee the buffer is long enough, always ensure there //! // are at least `T::FORMATTED_SIZE` bytes, which requires the //! // `lexical_core::FormattedSize` trait to be in scope. //! use lexical_core::FormattedSize; //! let mut buf = [b'0'; f64::FORMATTED_SIZE]; //! let slc = lexical_core::write::(15.1, &mut buf); //! assert_eq!(slc, b"15.1"); //! //! // When the `radix` feature is enabled, for decimal floats, using //! // `T::FORMATTED_SIZE` may significantly overestimate the space //! // required to format the number. Therefore, the //! // `T::FORMATTED_SIZE_DECIMAL` constants allow you to get a much //! // tighter bound on the space required. //! let mut buf = [b'0'; f64::FORMATTED_SIZE_DECIMAL]; //! let slc = lexical_core::write::(15.1, &mut buf); //! assert_eq!(slc, b"15.1"); //! # } //! ``` //! //! # Conversion API #![cfg_attr(feature = "write", doc = " **Write**")] #![cfg_attr(feature = "write", doc = "")] #![cfg_attr(feature = "write", doc = " - [`write`]")] #![cfg_attr(feature = "write", doc = " - [`write_with_options`]")] //! #![cfg_attr(feature = "write", doc = " **From String**")] #![cfg_attr(feature = "write", doc = "")] #![cfg_attr(feature = "parse", doc = " - [`parse`]")] #![cfg_attr(feature = "parse", doc = " - [`parse_partial`]")] #![cfg_attr(feature = "parse", doc = " - [`parse_with_options`]")] #![cfg_attr(feature = "parse", doc = " - [`parse_partial_with_options`]")] //! //! # Features //! //! In accordance with the Rust ethos, all features are additive: the crate //! may be build with `--all-features` without issue. The following features //! are enabled by default: //! //! * `std` //! * `write-integers` //! * `write-floats` //! * `parse-integers` //! * `parse-floats` //! //! A complete description of supported features includes: //! //! #### std //! //! Enable use of the standard library. Currently, the standard library //! is not used for any functionality, and may be disabled without any //! change in functionality on stable. //! //! #### write-integers //! //! Enable support for writing integers to string. //! //! #### write-floats //! //! Enable support for writing floating-point numbers to string. //! //! #### parse-integers //! //! Enable support for parsing integers from string. //! //! #### parsing-floats //! //! Enable support for parsing floating-point numbers from string. //! //! #### format //! //! Adds support for the entire format API (using [`NumberFormatBuilder`]). //! This allows extensive configurability for parsing and writing numbers //! in custom formats, with different valid syntax requirements. //! //! For example, in JSON, the following floats are valid or invalid: //! //! ```text //! -1 // valid //! +1 // invalid //! 1 // valid //! 1. // invalid //! .1 // invalid //! 0.1 // valid //! nan // invalid //! inf // invalid //! Infinity // invalid //! ``` //! //! All of the finite numbers are valid in Rust, and Rust provides constants //! for non-finite floats. In order to parse standard-conforming JSON floats //! using lexical, you may use the following approach: //! //! ```rust //! # #[cfg(all(feature = "parse-floats", feature = "format"))] { //! use lexical_core::{format, parse_with_options, ParseFloatOptions, Result}; //! //! fn parse_json_float>(bytes: Bytes) -> Result { //! let options = ParseFloatOptions::new(); //! parse_with_options::<_, { format::JSON }>(bytes.as_ref(), &options) //! } //! # } //! ``` //! //! See the [Number Format](#number-format) section below for more information. //! //! #### power-of-two //! //! Enable doing numeric conversions to and from strings with power-of-two //! radixes. This avoids most of the overhead and binary bloat of the radix //! feature, while enabling support for the most commonly-used radixes. //! //! #### radix //! //! Enable doing numeric conversions to and from strings for all radixes. //! This requires substantially more static storage than `power-of-two`, //! and increases compile times by a fair amount, but can be quite useful //! for esoteric programming languages which use duodecimal floats, for //! example. //! //! #### compact //! //! Reduce the generated code size at the cost of performance. This minimizes //! the number of static tables, inlining, and generics used, drastically //! reducing the size of the generated binaries. //! //! #### safe //! //! This replaces most unchecked indexing, required in cases where the //! compiler cannot elide the check, with checked indexing. However, //! it does not fully replace all unsafe behavior with safe behavior. //! To minimize the risk of undefined behavior and out-of-bounds reads/writers, //! extensive edge-cases, property-based tests, and fuzzing is done with both //! the safe feature enabled and disabled, with the tests verified by Miri //! and Valgrind. //! //! # Configuration API //! //! Lexical provides two main levels of configuration: //! - The [`NumberFormatBuilder`], creating a packed struct with custom //! formatting options. //! - The Options API. //! //! ## Number Format //! //! The number format class provides numerous flags to specify //! number 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. //! //! ## Options API //! //! The Options API provides high-level options to specify number parsing //! or writing, options not intrinsically tied to a number format. //! For example, the Options API provides: //! - The exponent character (default `b'e'`, or `b'^'`). //! - The decimal point character (default `b'.'`). //! - Custom `NaN`, `Infinity` string representations. //! - Whether to trim the fraction component from integral floats. //! - The exponent break point for scientific notation. //! - The maximum and minimum number of significant digits to write. //! - The rounding mode when truncating significant digits while writing. //! //! The available options are: #![cfg_attr(feature = "parse-floats", doc = " - [`ParseFloatOptions`]")] #![cfg_attr(feature = "parse-integers", doc = " - [`ParseIntegerOptions`]")] #![cfg_attr(feature = "write-floats", doc = " - [`WriteFloatOptions`]")] #![cfg_attr(feature = "write-integers", doc = " - [`WriteIntegerOptions`]")] //! //! In addition, pre-defined constants for each category of options may //! be found in their respective modules. //! //! ## Example //! //! An example of creating your own options to parse European-style //! numbers (which use commas as decimal points, and periods as digit //! separators) is as follows: //! //! ``` //! # pub fn main() { //! # #[cfg(all(feature = "parse_floats", feature = "format"))] { //! // This creates a format to parse a European-style float number. //! // The decimal point is a comma, and the digit separators (optional) //! // are periods. //! const EUROPEAN: u128 = lexical_core::NumberFormatBuilder::new() //! .digit_separator(b'.') //! .build(); //! let options = lexical_core::ParseFloatOptions::builder() //! .decimal_point(b',') //! .build() //! .unwrap(); //! assert_eq!( //! lexical_core::parse_with_options::(b"300,10", &options), //! Ok(300.10) //! ); //! //! // Another example, using a pre-defined constant for JSON. //! const JSON: u128 = lexical_core::format::JSON; //! let options = lexical_core::ParseFloatOptions::new(); //! assert_eq!( //! lexical_core::parse_with_options::(b"0e1", &options), //! Ok(0.0) //! ); //! assert_eq!( //! lexical_core::parse_with_options::(b"1E+2", &options), //! Ok(100.0) //! ); //! # } //! # } //! ``` //! //! # Algorithms //! //! - [Parsing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Algorithm.md) //! - [Parsing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Algorithm.md) //! - [Writing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Algorithm.md) //! - [Writing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Algorithm.md) //! //! # Benchmarks //! //! - [Parsing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Benchmarks.md) //! - [Parsing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Benchmarks.md) //! - [Writing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Benchmarks.md) //! - [Writing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Benchmarks.md) //! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks) //! //! A comprehensive analysis of lexical commits and their performance can be //! found in [benchmarks]. //! //! # Design //! //! - [Binary Size](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BinarySize.md) //! - [Build Timings](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BuildTimings.md) //! - [Digit Separators](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/DigitSeparators.md) //! //! # Version Support //! //! The minimum, standard, required version is 1.63.0, for const generic //! support. Older versions of lexical support older Rust versions. //! //! # Safety //! //! There is no non-trivial unsafe behavior in [lexical][crate] itself, //! however, any incorrect safety invariants in our parsers and writers //! (`lexical-parse-float`, `lexical-parse-integer`, `lexical-write-float`, //! and `lexical-write-integer`) could cause those safety invariants to //! be broken. //! //! [`write`]: crate::write //! [`write_with_options`]: crate::write_with_options //! [`parse`]: crate::parse //! [`parse_partial`]: crate::parse_partial //! [`parse_with_options`]: crate::parse_with_options //! [`parse_partial_with_options`]: crate::parse_partial_with_options //! //! [`NumberFormatBuilder`]: crate::NumberFormatBuilder //! [`ParseFloatOptions`]: crate::ParseFloatOptions //! [`ParseIntegerOptions`]: crate::ParseIntegerOptions //! [`WriteFloatOptions`]: crate::WriteFloatOptions //! [`WriteIntegerOptions`]: crate::WriteIntegerOptions //! [benchmarks]: // We want to have the same safety guarantees as Rust core, // so we allow unused unsafe to clearly document safety guarantees. #![allow(unused_unsafe)] #![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))] #![cfg_attr(not(feature = "std"), no_std)] #![deny( clippy::doc_markdown, clippy::unnecessary_safety_comment, clippy::semicolon_if_nothing_returned, clippy::unwrap_used, clippy::as_underscore, clippy::doc_markdown )] #![allow( // used when concepts are logically separate clippy::match_same_arms, // loss of precision is intentional clippy::integer_division, // mathematical names use 1-character identifiers clippy::min_ident_chars, // these are not cryptographically secure contexts clippy::integer_division_remainder_used, // this can be intentional clippy::module_name_repetitions, // this is intentional: already passing a pointer and need performance clippy::needless_pass_by_value, // we use this for inline formatting for unsafe blocks clippy::semicolon_inside_block, )] // Re-exports #[cfg(feature = "parse-floats")] pub use lexical_parse_float::{ options as parse_float_options, Options as ParseFloatOptions, OptionsBuilder as ParseFloatOptionsBuilder, }; #[cfg(feature = "parse-floats")] use lexical_parse_float::{ FromLexical as FromFloat, FromLexicalWithOptions as FromFloatWithOptions, }; #[cfg(feature = "parse-integers")] pub use lexical_parse_integer::{ options as parse_integer_options, Options as ParseIntegerOptions, OptionsBuilder as ParseIntegerOptionsBuilder, }; #[cfg(feature = "parse-integers")] use lexical_parse_integer::{ FromLexical as FromInteger, FromLexicalWithOptions as FromIntegerWithOptions, }; #[cfg(feature = "f16")] pub use lexical_util::bf16::bf16; #[cfg(feature = "write")] pub use lexical_util::constants::{FormattedSize, BUFFER_SIZE}; #[cfg(feature = "parse")] pub use lexical_util::error::Error; #[cfg(feature = "f16")] pub use lexical_util::f16::f16; pub use lexical_util::format::{self, format_error, format_is_valid, NumberFormatBuilder}; #[cfg(feature = "parse")] pub use lexical_util::options::ParseOptions; #[cfg(feature = "write")] pub use lexical_util::options::WriteOptions; #[cfg(feature = "parse")] pub use lexical_util::result::Result; #[cfg(feature = "parse")] use lexical_util::{from_lexical, from_lexical_with_options}; #[cfg(feature = "write")] use lexical_util::{to_lexical, to_lexical_with_options}; #[cfg(feature = "write-floats")] pub use lexical_write_float::{ options as write_float_options, Options as WriteFloatOptions, OptionsBuilder as WriteFloatOptionsBuilder, }; #[cfg(feature = "write-floats")] use lexical_write_float::{ToLexical as ToFloat, ToLexicalWithOptions as ToFloatWithOptions}; #[cfg(feature = "write-integers")] pub use lexical_write_integer::{ options as write_integer_options, Options as WriteIntegerOptions, OptionsBuilder as WriteIntegerOptionsBuilder, }; #[cfg(feature = "write-integers")] use lexical_write_integer::{ToLexical as ToInteger, ToLexicalWithOptions as ToIntegerWithOptions}; // API // --- #[cfg(feature = "parse")] from_lexical!(); #[cfg(feature = "parse")] from_lexical_with_options!(); #[cfg(feature = "write")] to_lexical!(); #[cfg(feature = "write")] to_lexical_with_options!(); /// Implement `FromLexical` and `FromLexicalWithOptions` for numeric types. /// /// * `t` - The numerical type. /// * `from` - The internal trait that implements /// `from_lexical`. /// * `from_lexical_with_options` - The internal trait that implements /// `from_lexical`. /// * `options` - The options type to configure settings. #[cfg(feature = "parse")] macro_rules! from_lexical_impl { ($t:ident, $from:ident, $from_options:ident, $options:ident) => { impl FromLexical for $t { #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical(bytes: &[u8]) -> Result { ::from_lexical(bytes) } #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical_partial(bytes: &[u8]) -> Result<(Self, usize)> { ::from_lexical_partial(bytes) } } impl FromLexicalWithOptions for $t { type Options = $options; #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical_with_options( bytes: &[u8], options: &Self::Options, ) -> Result { ::from_lexical_with_options::(bytes, options) } #[cfg_attr(not(feature = "compact"), inline)] fn from_lexical_partial_with_options( bytes: &[u8], options: &Self::Options, ) -> Result<(Self, usize)> { ::from_lexical_partial_with_options::(bytes, options) } } }; } /// Implement `FromLexical` and `FromLexicalWithOptions` for integers. #[cfg(feature = "parse-integers")] macro_rules! integer_from_lexical { ($($t:ident)*) => ($( from_lexical_impl!($t, FromInteger, FromIntegerWithOptions, ParseIntegerOptions); )*); } #[cfg(feature = "parse-integers")] integer_from_lexical! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } /// Implement `FromLexical` and `FromLexicalWithOptions` for floats. #[cfg(feature = "parse-floats")] macro_rules! float_from_lexical { ($($t:ident)*) => ($( from_lexical_impl!($t, FromFloat, FromFloatWithOptions, ParseFloatOptions); )*); } #[cfg(feature = "parse-floats")] float_from_lexical! { f32 f64 } /// Implement `ToLexical` and `ToLexicalWithOptions` for numeric types. /// /// * `t` - The numerical type. /// * `to` - The internal trait that implements /// `to_lexical`. /// * `to_lexical_with_options` - The internal trait that implements /// `to_lexical`. /// * `options` - The options type to configure settings. #[cfg(feature = "write")] macro_rules! to_lexical_impl { ($t:ident, $to:ident, $to_options:ident, $options:ident) => { impl ToLexical for $t { #[cfg_attr(not(feature = "compact"), inline)] fn to_lexical(self, bytes: &mut [u8]) -> &mut [u8] { ::to_lexical(self, bytes) } } impl ToLexicalWithOptions for $t { type Options = $options; #[cfg_attr(not(feature = "compact"), inline(always))] fn to_lexical_with_options<'a, const FORMAT: u128>( self, bytes: &'a mut [u8], options: &Self::Options, ) -> &'a mut [u8] { ::to_lexical_with_options::(self, bytes, options) } } }; } /// Implement `ToLexical` and `ToLexicalWithOptions` for integers. #[cfg(feature = "write-integers")] macro_rules! integer_to_lexical { ($($t:ident)*) => ($( to_lexical_impl!($t, ToInteger, ToIntegerWithOptions, WriteIntegerOptions); )*); } #[cfg(feature = "write-integers")] integer_to_lexical! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } /// Implement `ToLexical` and `ToLexicalWithOptions` for floats. #[cfg(feature = "write-floats")] macro_rules! float_to_lexical { ($($t:ident)*) => ($( to_lexical_impl!($t, ToFloat, ToFloatWithOptions, WriteFloatOptions); )*); } #[cfg(feature = "write-floats")] float_to_lexical! { f32 f64 } /// Write number to string. /// /// Returns a subslice of the input buffer containing the written bytes, /// starting from the same address in memory as the input slice. /// /// * `value` - Number to serialize. /// * `bytes` - Buffer to write number to. /// /// # Panics /// /// Panics if the buffer may not be large enough to hold the serialized /// number. In order to ensure the function will not panic, provide a /// buffer with at least `{integer}::FORMATTED_SIZE` elements. /// /// # Example /// /// ``` /// # pub fn main() { /// #[cfg(feature = "write-floats")] { /// // import `BUFFER_SIZE` to get the maximum bytes written by the number. /// use lexical_core::BUFFER_SIZE; /// /// let mut buffer = [0u8; BUFFER_SIZE]; /// let float = 3.14159265359_f32; /// /// lexical_core::write(float, &mut buffer); /// /// assert_eq!(&buffer[0..9], b"3.1415927"); /// # } /// # } /// ``` /// /// This will panic, because the buffer is not large enough: /// /// ```should_panic /// # #[cfg(feature = "write-floats")] { /// // note: the buffer is only one byte large /// let mut buffer = [0u8; 1]; /// let float = 3.14159265359_f32; /// /// lexical_core::write(float, &mut buffer); /// # } /// # #[cfg(not(feature = "write-floats"))] { /// # panic!(""); /// # } /// ``` #[inline] #[cfg(feature = "write")] pub fn write(n: N, bytes: &mut [u8]) -> &mut [u8] { n.to_lexical(bytes) } /// Write number to string with custom options. /// /// Returns a subslice of the input buffer containing the written bytes, /// starting from the same address in memory as the input slice. /// /// * `FORMAT` - Packed struct containing the number format. /// * `value` - Number to serialize. /// * `bytes` - Buffer to write number to. /// * `options` - Options to customize number parsing. /// /// # Panics /// /// Panics if the buffer may not be large enough to hold the serialized /// number. In order to ensure the function will not panic, provide a /// buffer with at least `{integer}::FORMATTED_SIZE` elements. If you /// are using custom digit precision control or exponent break points /// for writing floats, these constants may be insufficient to store /// the serialized number, and up to 1200 bytes may be required with /// radix support. /// /// If the provided `FORMAT` is not valid, the function may panic. Please /// ensure `is_valid()` is called prior to using the format, or checking /// its validity using a static assertion. /// /// # Example /// /// ``` /// # pub fn main() { /// #[cfg(feature = "write-floats")] { /// // import `BUFFER_SIZE` to get the maximum bytes written by the number. /// use lexical_core::BUFFER_SIZE; /// /// let mut buffer = [0u8; BUFFER_SIZE]; /// let float = 3.14159265359_f32; /// /// const FORMAT: u128 = lexical_core::format::STANDARD; /// let options = lexical_core::WriteFloatOptions::new(); /// lexical_core::write_with_options::<_, FORMAT>(float, &mut buffer, &options); /// /// assert_eq!(&buffer[0..9], b"3.1415927"); /// # } /// # } /// ``` /// /// This will panic, because the buffer is not large enough: /// /// ```should_panic /// # #[cfg(feature = "write-floats")] { /// // note: the buffer is only one byte large /// let mut buffer = [0u8; 1]; /// let float = 3.14159265359_f32; /// /// const FORMAT: u128 = lexical_core::format::STANDARD; /// let options = lexical_core::WriteFloatOptions::new(); /// lexical_core::write_with_options::<_, FORMAT>(float, &mut buffer, &options); /// # } /// # #[cfg(not(feature = "write-floats"))] { /// # panic!(""); /// # } /// ``` #[inline] #[cfg(feature = "write")] pub fn write_with_options<'a, N: ToLexicalWithOptions, const FORMAT: u128>( n: N, bytes: &'a mut [u8], options: &N::Options, ) -> &'a mut [u8] { n.to_lexical_with_options::(bytes, options) } /// Parse complete number from string. /// /// This method parses the entire string, returning an error if /// any invalid digits are found during parsing. /// /// * `bytes` - Byte slice containing a numeric string. /// /// # Example /// /// ``` /// # pub fn main() { /// #[cfg(feature = "parse-floats")] { /// let string = "3.14159265359"; /// let result = lexical_core::parse::(string.as_bytes()); /// assert_eq!(result, Ok(3.14159265359_f32)); /// # } /// # } /// ``` #[inline] #[cfg(feature = "parse")] pub fn parse(bytes: &[u8]) -> Result { N::from_lexical(bytes) } /// Parse partial number from string. /// /// This method parses until an invalid digit is found (or the end /// of the string), returning the number of processed digits /// and the parsed value until that point. /// /// * `bytes` - Byte slice containing a numeric string. /// /// # Example /// /// ``` /// # pub fn main() { /// #[cfg(feature = "parse-floats")] { /// let string = "3.14159265359 hello"; /// let result = lexical_core::parse_partial::(string.as_bytes()); /// assert_eq!(result, Ok((3.14159265359_f32, 13))); /// # } /// # } /// ``` #[inline] #[cfg(feature = "parse")] pub fn parse_partial(bytes: &[u8]) -> Result<(N, usize)> { N::from_lexical_partial(bytes) } /// Parse complete number from string with custom parsing options. /// /// This method parses the entire string, returning an error if /// any invalid digits are found during parsing. /// /// * `FORMAT` - Packed struct containing the number format. /// * `bytes` - Byte slice containing a numeric string. /// * `options` - Options to customize number parsing. /// /// # Example /// /// ``` /// # pub fn main() { /// #[cfg(all(feature = "parse-floats", feature = "format"))] { /// const JSON: u128 = lexical_core::format::JSON; /// let options = lexical_core::ParseFloatOptions::new(); /// let string = "3.14159265359"; /// let result = lexical_core::parse_with_options::(string.as_bytes(), &options); /// assert_eq!(result, Ok(3.14159265359_f32)); /// # } /// # } /// ``` #[inline] #[cfg(feature = "parse")] pub fn parse_with_options( bytes: &[u8], options: &N::Options, ) -> Result { N::from_lexical_with_options::(bytes, options) } /// Parse partial number from string with custom parsing options. /// /// This method parses until an invalid digit is found (or the end /// of the string), returning the number of processed digits /// and the parsed value until that point. /// /// * `FORMAT` - Packed struct containing the number format. /// * `bytes` - Byte slice containing a numeric string. /// * `options` - Options to customize number parsing. /// /// # Example /// /// ``` /// # pub fn main() { /// #[cfg(all(feature = "parse-floats", feature = "format"))] { /// const JSON: u128 = lexical_core::format::JSON; /// let options = lexical_core::ParseFloatOptions::new(); /// let string = "3.14159265359 hello"; /// let result = lexical_core::parse_partial_with_options::(string.as_bytes(), &options); /// assert_eq!(result, Ok((3.14159265359_f32, 13))); /// # } /// # } /// ``` #[inline] #[cfg(feature = "parse")] pub fn parse_partial_with_options( bytes: &[u8], options: &N::Options, ) -> Result<(N, usize)> { N::from_lexical_partial_with_options::(bytes, options) } lexical-core-1.0.5/tests/api_tests.rs000064400000000000000000000037451046102023000157120ustar 00000000000000#[test] #[cfg(feature = "write-integers")] fn integer_to_string_test() { let mut buffer = [b'0'; lexical_core::BUFFER_SIZE]; assert_eq!(lexical_core::write(12345u32, &mut buffer), b"12345"); let options = lexical_core::WriteIntegerOptions::new(); const FORMAT: u128 = lexical_core::format::STANDARD; assert_eq!( lexical_core::write_with_options::<_, FORMAT>(12345u32, &mut buffer, &options), b"12345" ); } #[test] #[cfg(feature = "write-floats")] fn float_to_string_test() { let mut buffer = [b'0'; lexical_core::BUFFER_SIZE]; assert_eq!(lexical_core::write(12345.0f32, &mut buffer), b"12345.0"); let options = lexical_core::WriteFloatOptions::new(); const FORMAT: u128 = lexical_core::format::STANDARD; assert_eq!( lexical_core::write_with_options::<_, FORMAT>(12345.0f32, &mut buffer, &options), b"12345.0" ); } #[test] #[cfg(feature = "parse-integers")] fn string_to_integer_test() { assert_eq!(lexical_core::parse(b"12345"), Ok(12345u32)); assert_eq!(lexical_core::parse_partial(b"12345"), Ok((12345u32, 5))); let options = lexical_core::ParseIntegerOptions::new(); const FORMAT: u128 = lexical_core::format::STANDARD; assert_eq!(lexical_core::parse_with_options::<_, FORMAT>(b"12345", &options), Ok(12345u32)); assert_eq!( lexical_core::parse_partial_with_options::<_, FORMAT>(b"12345", &options), Ok((12345u32, 5)) ); } #[test] #[cfg(feature = "parse-floats")] fn string_to_float_test() { assert_eq!(lexical_core::parse(b"12345.0"), Ok(12345.0f32)); assert_eq!(lexical_core::parse_partial(b"12345.0"), Ok((12345.0f32, 7))); let options = lexical_core::ParseFloatOptions::new(); const FORMAT: u128 = lexical_core::format::STANDARD; assert_eq!(lexical_core::parse_with_options::<_, FORMAT>(b"12345.0", &options), Ok(12345.0f32)); assert_eq!( lexical_core::parse_partial_with_options::<_, FORMAT>(b"12345.0", &options), Ok((12345.0f32, 7)) ); } lexical-core-1.0.5/tests/float_pow2_tests.rs000064400000000000000000000051461046102023000172120ustar 00000000000000#![cfg(feature = "power-of-two")] #![cfg(feature = "parse-floats")] #![cfg(feature = "write-floats")] use approx::assert_relative_eq; const F32_DATA: [f32; 31] = [ 0., 0.1, 1., 1.1, 12., 12.1, 123., 123.1, 1234., 1234.1, 12345., 12345.1, 123456., 123456.1, 1234567., 1234567.1, 12345678., 12345678.1, 123456789., 123456789.1, 123456789.12, 123456789.123, 123456789.1234, 123456789.12345, 1.2345678912345e8, 1.2345e+8, 1.2345e+11, 1.2345e+38, 1.2345e-8, 1.2345e-11, 1.2345e-38, ]; const F64_DATA: [f64; 33] = [ 0., 0.1, 1., 1.1, 12., 12.1, 123., 123.1, 1234., 1234.1, 12345., 12345.1, 123456., 123456.1, 1234567., 1234567.1, 12345678., 12345678.1, 123456789., 123456789.1, 123456789.12, 123456789.123, 123456789.1234, 123456789.12345, 1.2345678912345e8, 1.2345e+8, 1.2345e+11, 1.2345e+38, 1.2345e+308, 1.2345e-8, 1.2345e-11, 1.2345e-38, 1.2345e-299, ]; macro_rules! test_radix { ($f:ident, $radix:expr, $buffer:ident, $data:ident) => {{ use lexical_core::{ FromLexicalWithOptions, NumberFormatBuilder, ParseFloatOptions, ToLexicalWithOptions, WriteFloatOptions, }; const FORMAT: u128 = NumberFormatBuilder::from_radix($radix); println!("Testing radix {} for type {}...", $radix, stringify!($f)); let write_options = WriteFloatOptions::builder().exponent(b'^').build().unwrap(); let parse_options = ParseFloatOptions::builder().exponent(b'^').build().unwrap(); for &float in $data.iter() { let data = float.to_lexical_with_options::(&mut $buffer, &write_options); let roundtrip = $f::from_lexical_with_options::(data, &parse_options).unwrap(); assert_relative_eq!(float, roundtrip, epsilon = 1e-6, max_relative = 3e-6); } }}; } macro_rules! test_all { ($f:ident, $buffer:ident, $data:ident) => {{ test_radix!($f, 2, $buffer, $data); test_radix!($f, 4, $buffer, $data); test_radix!($f, 8, $buffer, $data); test_radix!($f, 16, $buffer, $data); test_radix!($f, 32, $buffer, $data); test_radix!($f, 36, $buffer, $data); }}; } #[test] fn parse_f32_pow2_roundtrip_test() { let mut buffer = [0u8; 1024]; test_all!(f32, buffer, F32_DATA); } #[test] fn parse_f64_pow2_roundtrip_test() { let mut buffer = [0u8; 1024]; test_all!(f64, buffer, F64_DATA); } lexical-core-1.0.5/tests/float_radix_tests.rs000064400000000000000000000075651046102023000174410ustar 00000000000000#![cfg(feature = "radix")] #![cfg(feature = "parse-floats")] #![cfg(feature = "write-floats")] use approx::assert_relative_eq; const F32_DATA: [f32; 31] = [ 0., 0.1, 1., 1.1, 12., 12.1, 123., 123.1, 1234., 1234.1, 12345., 12345.1, 123456., 123456.1, 1234567., 1234567.1, 12345678., 12345678.1, 123456789., 123456789.1, 123456789.12, 123456789.123, 123456789.1234, 123456789.12345, 1.2345678912345e8, 1.2345e+8, 1.2345e+11, 1.2345e+38, 1.2345e-8, 1.2345e-11, 1.2345e-38, ]; const F64_DATA: [f64; 33] = [ 0., 0.1, 1., 1.1, 12., 12.1, 123., 123.1, 1234., 1234.1, 12345., 12345.1, 123456., 123456.1, 1234567., 1234567.1, 12345678., 12345678.1, 123456789., 123456789.1, 123456789.12, 123456789.123, 123456789.1234, 123456789.12345, 1.2345678912345e8, 1.2345e+8, 1.2345e+11, 1.2345e+38, 1.2345e+308, 1.2345e-8, 1.2345e-11, 1.2345e-38, 1.2345e-299, ]; macro_rules! test_radix { ($f:ident, $radix:expr, $buffer:ident, $data:ident) => {{ use lexical_core::{ FromLexicalWithOptions, NumberFormatBuilder, ParseFloatOptions, ToLexicalWithOptions, WriteFloatOptions, }; const FORMAT: u128 = NumberFormatBuilder::from_radix($radix); println!("Testing radix {} for type {}...", $radix, stringify!($f)); let write_options = WriteFloatOptions::builder().exponent(b'^').build().unwrap(); let parse_options = ParseFloatOptions::builder().exponent(b'^').build().unwrap(); for &float in $data.iter() { let data = float.to_lexical_with_options::(&mut $buffer, &write_options); let roundtrip = $f::from_lexical_with_options::(data, &parse_options).unwrap(); assert_relative_eq!(float, roundtrip, epsilon = 1e-6, max_relative = 3e-6); } }}; } macro_rules! test_all { ($f:ident, $buffer:ident, $data:ident) => {{ test_radix!($f, 2, $buffer, $data); test_radix!($f, 3, $buffer, $data); test_radix!($f, 4, $buffer, $data); test_radix!($f, 5, $buffer, $data); test_radix!($f, 6, $buffer, $data); test_radix!($f, 7, $buffer, $data); test_radix!($f, 8, $buffer, $data); test_radix!($f, 9, $buffer, $data); test_radix!($f, 19, $buffer, $data); test_radix!($f, 11, $buffer, $data); test_radix!($f, 12, $buffer, $data); test_radix!($f, 13, $buffer, $data); test_radix!($f, 14, $buffer, $data); test_radix!($f, 15, $buffer, $data); test_radix!($f, 16, $buffer, $data); test_radix!($f, 17, $buffer, $data); test_radix!($f, 18, $buffer, $data); test_radix!($f, 19, $buffer, $data); test_radix!($f, 20, $buffer, $data); test_radix!($f, 21, $buffer, $data); test_radix!($f, 22, $buffer, $data); test_radix!($f, 23, $buffer, $data); test_radix!($f, 24, $buffer, $data); test_radix!($f, 25, $buffer, $data); test_radix!($f, 26, $buffer, $data); test_radix!($f, 27, $buffer, $data); test_radix!($f, 28, $buffer, $data); test_radix!($f, 29, $buffer, $data); test_radix!($f, 30, $buffer, $data); test_radix!($f, 31, $buffer, $data); test_radix!($f, 32, $buffer, $data); test_radix!($f, 33, $buffer, $data); test_radix!($f, 34, $buffer, $data); test_radix!($f, 35, $buffer, $data); test_radix!($f, 36, $buffer, $data); }}; } #[test] fn parse_f32_radix_roundtrip_test() { let mut buffer = [0u8; 1024]; test_all!(f32, buffer, F32_DATA); } #[test] fn parse_f64_radix_roundtrip_test() { let mut buffer = [0u8; 1024]; test_all!(f64, buffer, F64_DATA); } lexical-core-1.0.5/tests/issue_97_tests.rs000064400000000000000000000037001046102023000165770ustar 00000000000000#![cfg(all(feature = "parse", feature = "format"))] use core::num; use lexical_core::{Error, FromLexical, FromLexicalWithOptions, NumberFormatBuilder}; #[test] fn issue_97_test() { const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) .build(); let fopts = lexical_core::ParseFloatOptions::new(); let iopts = lexical_core::ParseIntegerOptions::new(); assert_eq!( i64::from_lexical_with_options::(b"_1234", &iopts), Err(Error::InvalidDigit(0)) ); assert_eq!( i64::from_lexical_with_options::(b"1234_", &iopts), Err(Error::InvalidDigit(4)) ); assert_eq!( f64::from_lexical_with_options::(b"_1234", &fopts), Err(Error::InvalidDigit(0)) ); assert_eq!( f64::from_lexical_with_options::(b"1234_", &fopts), Err(Error::InvalidDigit(4)) ); assert_eq!( f64::from_lexical_with_options::(b"_12.34", &fopts), Err(Error::InvalidDigit(0)) ); assert_eq!( f64::from_lexical_with_options::(b"12.34_", &fopts), Err(Error::InvalidDigit(5)) ); assert_eq!(f64::from_lexical_with_options::(b"1_2.34", &fopts), Ok(12.34)); } #[test] fn issue_97_nofmt_test() { assert_eq!(i64::from_lexical(b"_1234"), Err(Error::InvalidDigit(0))); assert_eq!(i64::from_lexical(b"1234_"), Err(Error::InvalidDigit(4))); assert_eq!(f64::from_lexical(b"_1234"), Err(Error::InvalidDigit(0))); assert_eq!(f64::from_lexical(b"1234_"), Err(Error::InvalidDigit(4))); assert_eq!(f64::from_lexical(b"_12.34"), Err(Error::InvalidDigit(0))); assert_eq!(f64::from_lexical(b"12.34_"), Err(Error::InvalidDigit(5))); assert_eq!(f64::from_lexical(b"_.34"), Err(Error::InvalidDigit(0))); assert_eq!(f64::from_lexical(b"0_0.34"), Err(Error::InvalidDigit(1))); assert_eq!(f64::from_lexical(b".34"), Ok(0.34)); }