scientific 0.2.0

Arbitrary precision scientific number (no_std capable, in pure Rust)
Documentation
use crate::ptr::Ptr;
use crate::types::builder::Builder;
use crate::types::conversion_error::ConversionError;
use crate::types::owner::Owner;
use crate::types::sci::Sci;
use crate::types::sign::Sign;
use alloc::string::String;
use core::str::FromStr;

impl Sci {
  pub(crate) fn from_string(mut data: String) -> Result<Sci, ConversionError> {
    let len = data.len() as isize;
    let mut data_start = Ptr::new_mut(unsafe { data.as_bytes_mut() });
    let data_end = data_start.offset(len);

    // check if len > 0
    if data_start == data_end {
      return Err(ConversionError::ParseError);
    }
    // remove sign if any
    let next = *data_start as u8;
    let sign = Sign::new(next == b'-');
    if sign.is_negative() || next == b'+' {
      data_start.inc();
    }

    // check if len > 0 and first char is 0-9 or .
    if data_start == data_end {
      return Err(ConversionError::ParseError);
    }
    let next = *data_start as u8;
    if !((b'0'..=b'9').contains(&next) || next == b'.') {
      return Err(ConversionError::ParseError);
    }

    let mut data_ptr = data_start;
    while data_ptr != data_end {
      let next = *data_ptr as u8;
      if !(b'0'..=b'9').contains(&next) {
        break;
      }
      *data_ptr = (next & 0x0f) as i8; // convert ascii to decimal
      data_ptr.inc();
    }

    let mut mantissa_end = data_ptr;

    let mut dot_len = 0;
    let exponent_start;
    let exponent_len;
    if data_ptr == data_end {
      // end of input = neither dot not exp
      exponent_start = data_end;
      exponent_len = 0;
    } else {
      let next = *data_ptr as u8;
      if next == b'.' {
        // found a dot
        data_ptr.inc();
        while data_ptr != data_end {
          let next = *data_ptr as u8;
          if !(b'0'..=b'9').contains(&next) {
            break;
          }
          *mantissa_end = (next & 0x0f) as i8; // convert ascii to decimal
          mantissa_end.inc();
          data_ptr.inc();
          dot_len += 1;
        }
      }
      if data_ptr == data_end {
        // no exp
        exponent_start = data_end;
        exponent_len = 0;
      } else {
        // check for exp
        let next = *data_ptr as u8;
        if !(next == b'e' || next == b'E') {
          return Err(ConversionError::ParseError);
        }
        data_ptr.inc();
        exponent_start = data_ptr;
        exponent_len = data_end.offset_from(data_ptr);
        if exponent_len == 0 {
          // specified 'e' but nothing behind it
          return Err(ConversionError::ParseError);
        }
      }
    }

    let exponent = if exponent_len == 0 {
      0
    } else {
      isize::from_str(unsafe {
        core::str::from_utf8_unchecked(core::slice::from_raw_parts(
          exponent_start.as_slice(exponent_len).as_ptr(),
          exponent_len as usize,
        ))
      })
      .map_err(|_| ConversionError::ParseError)?
    };

    if data_start == mantissa_end {
      // no digits given (neither before or after the dot)
      return Err(ConversionError::ParseError);
    }

    let len = mantissa_end.offset_from(data_start);

    Ok(
      Builder::new_with_data(
        sign,
        data_start,
        len,
        exponent - dot_len,
        Owner::new_string(data),
      )
      .finish(),
    )
  }
}