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);
}
let (sign, mut cursor) = match input.first() {
Some(b'+') => (1, 1),
Some(b'-') => (-1, 1),
_ => (1, 0),
let (sign, mut input) = match input.first() {
Some(b'+') => (1, &input[1..]),
Some(b'-') => (-1, &input[1..]),
_ => (1, input),
};
let mut value = 0_i128;
let mut with_before_dot = false;
while cursor < input.len() && b'0' <= input[cursor] && input[cursor] <= b'9' {
value = value
.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)?;
let with_before_dot = input.first().map_or(false, |c| c.is_ascii_digit());
while let Some(c) = input.first() {
if c.is_ascii_digit() {
value = value
.checked_mul(10)
.ok_or(PARSE_OVERFLOW)?
.checked_add((input[cursor] - b'0').into())
.checked_add((*c - b'0').into())
.ok_or(PARSE_OVERFLOW)?;
cursor += 1;
with_after_dot = true;
input = &input[1..];
} 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
return Err(PARSE_UNEXPECTED_END);
}
if input.len() > cursor {
return Err(PARSE_UNEXPECTED_CHAR);
while input.last() == Some(&b'0') {
// 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 {
//It's empty
@ -591,6 +599,14 @@ mod tests {
#[test]
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("1000")?.to_string(), "1000");
assert_eq!(Decimal::from_str("-1.23")?.to_string(), "-1.23");
@ -616,6 +632,12 @@ mod tests {
)?,
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(())
}

Loading…
Cancel
Save