#![deny(unsafe_code)]
#![deny(missing_docs)]
#![warn(clippy::dbg_macro)]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::fmt;
mod parse_utils;
use parse_utils::*;
mod fpformat;
pub use fpformat::FPFormat;
#[derive(Debug)]
pub enum ConversionResult<T> {
Precise(T),
Imprecise(T),
}
impl<T> ConversionResult<T> {
pub fn inner(self) -> T {
match self {
ConversionResult::Precise(f) => f,
ConversionResult::Imprecise(f) => f,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct ParseError {
pub kind: ParseErrorKind,
pub index: usize,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ParseErrorKind {
MissingPrefix,
MissingDigits,
MissingExponent,
ExponentOverflow,
MissingEnd,
}
impl ParseErrorKind {
fn at(self, index: usize) -> ParseError {
ParseError { kind: self, index }
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind {
ParseErrorKind::MissingPrefix => write!(f, "literal must have hex prefix"),
ParseErrorKind::MissingDigits => write!(f, "literal must have digits"),
ParseErrorKind::MissingExponent => write!(f, "exponent not present"),
ParseErrorKind::ExponentOverflow => write!(f, "exponent too large to fit in integer"),
ParseErrorKind::MissingEnd => {
write!(f, "extra bytes were found at the end of float literal")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseError {}
#[derive(Debug, Clone)]
pub struct FloatLiteral {
is_positive: bool,
digits: Vec<u8>,
decimal_offset: i32,
exponent: i32,
}
fn get_cursed_index(master_slice: &[u8], sub_slice: &[u8]) -> usize {
(sub_slice.as_ptr() as usize).saturating_sub(master_slice.as_ptr() as usize)
}
impl FloatLiteral {
pub fn convert<F: FPFormat>(self) -> ConversionResult<F> {
F::from_literal(self)
}
pub fn from_bytes(data: &[u8]) -> Result<FloatLiteral, ParseError> {
let original_data = data;
let (is_positive, data) = match data.get(0) {
Some(b'+') => (true, &data[1..]),
Some(b'-') => (false, &data[1..]),
_ => (true, data),
};
let data = match data.get(0..2) {
Some(b"0X") | Some(b"0x") => &data[2..],
_ => return Err(ParseErrorKind::MissingPrefix.at(0)),
};
let (ipart, data) = consume_hex_digits(data);
let (fpart, data): (&[_], _) = if data.get(0) == Some(&b'.') {
let (fpart, data) = consume_hex_digits(&data[1..]);
(fpart, data)
} else {
(b"", data)
};
if fpart.is_empty() && ipart.is_empty() {
return Err(ParseErrorKind::MissingDigits.at(get_cursed_index(original_data, data)));
}
let (exponent, data) = match data.get(0) {
Some(b'P') | Some(b'p') => {
let data = &data[1..];
let sign_offset = match data.get(0) {
Some(b'+') | Some(b'-') => 1,
_ => 0,
};
let exponent_digits_offset = data[sign_offset..]
.iter()
.position(|&b| match b {
b'0'..=b'9' => false,
_ => true,
})
.unwrap_or_else(|| data[sign_offset..].len());
if exponent_digits_offset == 0 {
return Err(
ParseErrorKind::MissingExponent.at(get_cursed_index(original_data, data))
);
}
let exponent: i32 =
core::str::from_utf8(&data[..sign_offset + exponent_digits_offset])
.expect("exponent did not contain valid utf-8")
.parse()
.map_err(|_| {
ParseErrorKind::ExponentOverflow
.at(get_cursed_index(original_data, data))
})?;
(exponent, &data[sign_offset + exponent_digits_offset..])
}
_ => (0, data),
};
if !data.is_empty() {
return Err(ParseErrorKind::MissingEnd.at(get_cursed_index(original_data, data)));
}
let mut raw_digits = ipart.to_vec();
raw_digits.extend_from_slice(fpart);
let first_digit = raw_digits.iter().position(|&d| d != b'0');
let (digits, decimal_offset) = if let Some(first_digit) = first_digit {
let last_digit = raw_digits.iter().rposition(|&d| d != b'0').unwrap();
let decimal_offset = (ipart.len() as i32) - (first_digit as i32);
raw_digits.truncate(last_digit + 1);
raw_digits.drain(..first_digit);
for item in raw_digits.iter_mut() {
*item = hex_digit_to_int(*item).unwrap();
}
(raw_digits, decimal_offset)
} else {
(Vec::new(), 0)
};
Ok(FloatLiteral {
is_positive,
digits,
decimal_offset,
exponent,
})
}
}
impl core::str::FromStr for FloatLiteral {
type Err = ParseError;
fn from_str(s: &str) -> Result<FloatLiteral, ParseError> {
FloatLiteral::from_bytes(s.as_bytes())
}
}
impl From<FloatLiteral> for f32 {
fn from(literal: FloatLiteral) -> f32 {
literal.convert().inner()
}
}
impl From<FloatLiteral> for f64 {
fn from(literal: FloatLiteral) -> f64 {
literal.convert().inner()
}
}
#[cfg(test)]
mod tests;