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. 72
      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() {
if c.is_ascii_digit() {
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_before_dot = true; } else {
break;
}
} }
let mut exp = DECIMAL_PART_POW; let mut exp = DECIMAL_PART_POW;
if input.len() > cursor { if let Some(c) = input.first() {
if input[cursor] != b'.' { if *c != b'.' {
return Err(PARSE_UNEXPECTED_CHAR); return Err(PARSE_UNEXPECTED_CHAR);
} }
cursor += 1; input = &input[1..];
if input.is_empty() && !with_before_dot {
let mut with_after_dot = false; //We only have a dot
while cursor < input.len() && b'0' <= input[cursor] && input[cursor] <= b'9' { return Err(PARSE_UNEXPECTED_END);
exp = exp.checked_div(10).ok_or(PARSE_UNDERFLOW)?; }
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 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 {
return Err(PARSE_UNEXPECTED_CHAR);
} }
if !with_before_dot && !with_after_dot {
//We only have a dot
return Err(PARSE_UNEXPECTED_END);
} }
if input.len() > cursor { if exp == 0 {
return Err(PARSE_UNEXPECTED_CHAR); //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