xsd:decimal parsing: properly detect underflow

checked_div is only failing on division by 0...
pull/467/head
Tpt 1 year ago committed by Thomas Tanon
parent 284e79521d
commit d587d3b2bb
  1. 84
      lib/oxsdatatypes/src/decimal.rs

@ -370,49 +370,57 @@ impl FromStr for Decimal {
return Err(PARSE_UNEXPECTED_END); return Err(PARSE_UNEXPECTED_END);
} }
let (sign, mut cursor) = match input.first() { let (sign, mut input) = match input.first() {
Some(b'+') => (1, 1), Some(b'+') => (1, &input[1..]),
Some(b'-') => (-1, 1), Some(b'-') => (-1, &input[1..]),
_ => (1, 0), _ => (1, input),
}; };
let mut value = 0_i128; let mut value = 0_i128;
let mut with_before_dot = false; let with_before_dot = input.first().map_or(false, |c| c.is_ascii_digit());
while cursor < input.len() && b'0' <= input[cursor] && input[cursor] <= b'9' { while let Some(c) = input.first() {
value = value if c.is_ascii_digit() {
.checked_mul(10)
.ok_or(PARSE_OVERFLOW)?
.checked_add((input[cursor] - b'0').into())
.ok_or(PARSE_OVERFLOW)?;
cursor += 1;
with_before_dot = true;
}
let mut exp = DECIMAL_PART_POW;
if input.len() > cursor {
if input[cursor] != b'.' {
return Err(PARSE_UNEXPECTED_CHAR);
}
cursor += 1;
let mut with_after_dot = false;
while cursor < input.len() && b'0' <= input[cursor] && input[cursor] <= b'9' {
exp = exp.checked_div(10).ok_or(PARSE_UNDERFLOW)?;
value = value value = value
.checked_mul(10) .checked_mul(10)
.ok_or(PARSE_OVERFLOW)? .ok_or(PARSE_OVERFLOW)?
.checked_add((input[cursor] - b'0').into()) .checked_add((*c - b'0').into())
.ok_or(PARSE_OVERFLOW)?; .ok_or(PARSE_OVERFLOW)?;
cursor += 1; input = &input[1..];
with_after_dot = true; } else {
break;
} }
}
if !with_before_dot && !with_after_dot { let mut exp = DECIMAL_PART_POW;
if let Some(c) = input.first() {
if *c != b'.' {
return Err(PARSE_UNEXPECTED_CHAR);
}
input = &input[1..];
if input.is_empty() && !with_before_dot {
//We only have a dot //We only have a dot
return Err(PARSE_UNEXPECTED_END); return Err(PARSE_UNEXPECTED_END);
} }
if input.len() > cursor { while input.last() == Some(&b'0') {
return Err(PARSE_UNEXPECTED_CHAR); // Hack to avoid underflows
input = &input[..input.len() - 1];
}
while let Some(c) = input.first() {
if c.is_ascii_digit() {
exp /= 10;
value = value
.checked_mul(10)
.ok_or(PARSE_OVERFLOW)?
.checked_add((*c - b'0').into())
.ok_or(PARSE_OVERFLOW)?;
input = &input[1..];
} else {
return Err(PARSE_UNEXPECTED_CHAR);
}
}
if exp == 0 {
//Underflow
return Err(PARSE_UNDERFLOW);
} }
} else if !with_before_dot { } else if !with_before_dot {
//It's empty //It's empty
@ -591,6 +599,14 @@ mod tests {
#[test] #[test]
fn from_str() -> Result<(), ParseDecimalError> { fn from_str() -> Result<(), ParseDecimalError> {
assert!(Decimal::from_str("").is_err());
assert!(Decimal::from_str("+").is_err());
assert!(Decimal::from_str("-").is_err());
assert!(Decimal::from_str(".").is_err());
assert!(Decimal::from_str("+.").is_err());
assert!(Decimal::from_str("-.").is_err());
assert!(Decimal::from_str("a").is_err());
assert!(Decimal::from_str(".a").is_err());
assert_eq!(Decimal::from_str("210")?.to_string(), "210"); assert_eq!(Decimal::from_str("210")?.to_string(), "210");
assert_eq!(Decimal::from_str("1000")?.to_string(), "1000"); assert_eq!(Decimal::from_str("1000")?.to_string(), "1000");
assert_eq!(Decimal::from_str("-1.23")?.to_string(), "-1.23"); assert_eq!(Decimal::from_str("-1.23")?.to_string(), "-1.23");
@ -616,6 +632,12 @@ mod tests {
)?, )?,
Decimal::MIN.checked_add(Decimal::step()).unwrap() Decimal::MIN.checked_add(Decimal::step()).unwrap()
); );
assert!(Decimal::from_str("0.0000000000000000001").is_err());
assert!(Decimal::from_str("1000000000000000000000").is_err());
assert_eq!(
Decimal::from_str("0.100000000000000000000000000").unwrap(),
Decimal::from_str("0.1").unwrap()
);
Ok(()) Ok(())
} }

Loading…
Cancel
Save