Skip to main content

fast_float2/
lib.rs

1//! This crate provides a super-fast decimal number parser from strings into
2//! floats.
3//!
4//! ## Usage
5//!
6//! There's two top-level functions provided: [`parse`](crate::parse()) and
7//! [`parse_partial`](crate::parse_partial()), both taking
8//! either a string or a bytes slice and parsing the input into either `f32` or
9//! `f64`:
10//!
11//! - [`parse`](crate::parse()) treats the whole string as a decimal number and
12//!   returns an error if there are invalid characters or if the string is
13//!   empty.
14//! - [`parse_partial`](crate::parse_partial()) tries to find the longest
15//!   substring at the beginning of the given input string that can be parsed as
16//!   a decimal number and, in the case of success, returns the parsed value
17//!   along the number of characters processed; an error is returned if the
18//!   string doesn't start with a decimal number or if it is empty. This
19//!   function is most useful as a building block when constructing more complex
20//!   parsers, or when parsing streams of data.
21//!
22//! ## Examples
23//!
24//! ```rust
25//! // Parse the entire string as a decimal number.
26//! let s = "1.23e-02";
27//! let x: f32 = fast_float2::parse(s).unwrap();
28//! assert_eq!(x, 0.0123);
29//!
30//! // Parse as many characters as possible as a decimal number.
31//! let s = "1.23e-02foo";
32//! let (x, n) = fast_float2::parse_partial::<f32, _>(s).unwrap();
33//! assert_eq!(x, 0.0123);
34//! assert_eq!(n, 8);
35//! assert_eq!(&s[n..], "foo");
36//! ```
37
38#![cfg_attr(not(feature = "std"), no_std)]
39#![allow(unused_unsafe)]
40#![warn(unsafe_op_in_unsafe_fn)]
41#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
42#![deny(
43    clippy::doc_markdown,
44    clippy::unnecessary_safety_comment,
45    clippy::semicolon_if_nothing_returned,
46    clippy::unwrap_used,
47    clippy::as_underscore
48)]
49#![allow(
50    clippy::cast_possible_truncation,
51    clippy::cast_possible_wrap,
52    clippy::cast_sign_loss,
53    clippy::cast_lossless,
54    clippy::cast_precision_loss,
55    clippy::missing_const_for_fn,
56    clippy::use_self,
57    clippy::module_name_repetitions,
58    clippy::cargo_common_metadata,
59    clippy::struct_field_names,
60    clippy::negative_feature_names
61)]
62
63use core::fmt::{self, Display};
64
65#[cfg(feature = "no-panic")]
66use no_panic::no_panic;
67
68mod binary;
69mod common;
70mod decimal;
71mod float;
72mod index;
73mod number;
74mod parse;
75mod simple;
76mod table;
77
78/// Opaque error type for fast-float parsing functions.
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
80pub struct Error;
81
82impl Display for Error {
83    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84        write!(f, "error while parsing a float")
85    }
86}
87
88#[cfg(feature = "std")]
89impl std::error::Error for Error {
90    fn description(&self) -> &'static str {
91        "error while parsing a float"
92    }
93}
94
95/// Result type alias for fast-float parsing functions.
96pub type Result<T> = core::result::Result<T, Error>;
97
98/// Trait for numerical float types that can be parsed from string.
99pub trait FastFloat: float::Float {
100    /// Parse a decimal number from string into float (full).
101    ///
102    /// # Errors
103    ///
104    /// Will return an error either if the string is not a valid decimal number.
105    /// or if any characters are left remaining unparsed.
106    #[inline]
107    fn parse_float<S: AsRef<[u8]>>(s: S) -> Result<Self> {
108        let s = s.as_ref();
109        match Self::parse_float_partial(s) {
110            Ok((v, n)) if n == s.len() => Ok(v),
111            _ => Err(Error),
112        }
113    }
114
115    /// Parse a decimal number from string into float (partial).
116    ///
117    /// This method parses as many characters as possible and returns the
118    /// resulting number along with the number of digits processed (in case
119    /// of success, this number is always positive).
120    ///
121    /// # Errors
122    ///
123    /// Will return an error either if the string doesn't start with a valid
124    /// decimal number – that is, if no zero digits were processed.
125    #[inline]
126    fn parse_float_partial<S: AsRef<[u8]>>(s: S) -> Result<(Self, usize)> {
127        parse::parse_float(s.as_ref()).ok_or(Error)
128    }
129}
130
131impl FastFloat for f32 {
132}
133impl FastFloat for f64 {
134}
135
136/// Parse a decimal number from string into float (full).
137///
138/// # Errors
139///
140/// Will return an error either if the string is not a valid decimal number
141/// or if any characters are left remaining unparsed.
142#[inline]
143#[cfg_attr(feature = "no-panic", no_panic)]
144pub fn parse<T: FastFloat, S: AsRef<[u8]>>(s: S) -> Result<T> {
145    T::parse_float(s)
146}
147
148/// Parse a decimal number from string into float (partial).
149///
150/// This function parses as many characters as possible and returns the
151/// resulting number along with the number of digits processed (in case of
152/// success, this number is always positive).
153///
154/// # Errors
155///
156/// Will return an error either if the string doesn't start with a valid decimal
157/// number – that is, if no zero digits were processed.
158#[inline]
159#[cfg_attr(feature = "no-panic", no_panic)]
160pub fn parse_partial<T: FastFloat, S: AsRef<[u8]>>(s: S) -> Result<(T, usize)> {
161    T::parse_float_partial(s)
162}