use std::io::BufRead;
use std::io::Cursor;
use std::io::ErrorKind;
use std::io::Result;
use lexical_core::FromLexical;
pub trait ReadNumberExt {
fn read_int_text<T: FromLexical>(&mut self) -> Result<T>;
fn read_float_text<T: FromLexical>(&mut self) -> Result<T>;
}
pub fn collect_binary_number(buffer: &[u8]) -> usize {
let mut index = 0;
let len = buffer.len();
for _ in 0..len {
match buffer[index] {
b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F' => {
index += 1;
}
_ => {
break;
}
}
}
index
}
pub fn collect_number(buffer: &[u8]) -> (usize, usize) {
let mut has_number = false;
let mut index = 0;
let len = buffer.len();
let mut point_pos = len;
for _ in 0..len {
match buffer[index] {
b'0'..=b'9' => {
has_number = true;
}
b'-' | b'+' => {
if has_number {
break;
}
}
b'.' => {
point_pos = index;
index += 1;
break;
}
_ => {
break;
}
}
index += 1;
}
if point_pos < len {
while index < len && buffer[index].is_ascii_digit() {
index += 1;
}
}
let mut is_scientific = false;
if has_number && index < len && (buffer[index] == b'e' || buffer[index] == b'E') {
is_scientific = true;
index += 1;
if index < len && (buffer[index] == b'-' || buffer[index] == b'+') {
index += 1
}
while index < len && buffer[index].is_ascii_digit() {
index += 1;
}
}
let effective = if !is_scientific
&& point_pos < len
&& buffer[point_pos + 1..index].iter().all(|x| *x == b'0')
{
point_pos
} else {
index
};
(index, effective)
}
#[inline]
fn read_num_text_exact<T: FromLexical>(buf: &[u8]) -> Result<T> {
match FromLexical::from_lexical(buf) {
Ok(value) => Ok(value),
Err(cause) => Err(std::io::Error::new(
ErrorKind::InvalidData,
format!(
"Cannot parse value:{:?} to number type, cause: {:?}",
String::from_utf8_lossy(buf),
cause
),
)),
}
}
impl<B> ReadNumberExt for Cursor<B>
where
B: AsRef<[u8]>,
{
fn read_int_text<T: FromLexical>(&mut self) -> Result<T> {
let buf = self.fill_buf()?;
let (n_in, n_out) = collect_number(buf);
if n_in == 0 {
return Err(std::io::Error::new(
ErrorKind::InvalidData,
"Failed to parse number from text: input does not contain a valid numeric format."
.to_string(),
));
}
let n = read_num_text_exact(&buf[..n_out])?;
self.consume(n_in);
Ok(n)
}
fn read_float_text<T: FromLexical>(&mut self) -> Result<T> {
let buf = self.fill_buf()?;
let (n_in, n_out) = collect_number(buf);
if n_in == 0 {
return Err(std::io::Error::new(
ErrorKind::InvalidData,
"Unable to parse float: provided text is not in a recognizable floating-point format.".to_string()
));
}
let buf = self.fill_buf()?;
let n = read_num_text_exact(&buf[..n_out])?;
self.consume(n_in);
Ok(n)
}
}