MCPcopy Create free account
hub / github.com/apache/arrow-rs / parse_decimal

Function parse_decimal

arrow-cast/src/parse.rs:875–1007  ·  view source on GitHub ↗

Parse the string format decimal value to i128/i256 format and checking the precision and scale. Expected behavior: - The result value can't be out of bounds. - When parsing a decimal with scale 0, all fractional digits will be discarded. The final fractional digits may be a subset or a superset of the digits after the decimal point when e-notation is used.

(
    s: &str,
    precision: u8,
    scale: i8,
)

Source from the content-addressed store, hash-verified

873/// fractional digits may be a subset or a superset of the digits after the decimal point when
874/// e-notation is used.
875pub fn parse_decimal<T: DecimalType>(
876 s: &str,
877 precision: u8,
878 scale: i8,
879) -> Result<T::Native, ArrowError> {
880 let mut result = T::Native::usize_as(0);
881 let mut fractionals: i8 = 0;
882 let mut digits: u8 = 0;
883 let base = T::Native::usize_as(10);
884
885 let bs = s.as_bytes();
886
887 if !bs
888 .last()
889 .is_some_and(|b| b.is_ascii_digit() || (b == &b'.' && s.len() > 1))
890 {
891 // If the last character is not a digit (or a decimal point prefixed with some digits), then
892 // it's not a valid decimal.
893 return Err(ArrowError::ParseError(format!(
894 "can't parse the string value {s} to decimal"
895 )));
896 }
897
898 let (signed, negative) = match bs.first() {
899 Some(b'-') => (true, true),
900 Some(b'+') => (true, false),
901 _ => (false, false),
902 };
903
904 // Iterate over the raw input bytes, skipping the sign if any
905 let mut bs = bs.iter().enumerate().skip(signed as usize);
906
907 let mut is_e_notation = false;
908
909 // Overflow checks are not required if 10^(precision - 1) <= T::MAX holds.
910 // Thus, if we validate the precision correctly, we can skip overflow checks.
911 while let Some((index, b)) = bs.next() {
912 match b {
913 b'0'..=b'9' => {
914 if digits == 0 && *b == b'0' {
915 // Ignore leading zeros.
916 continue;
917 }
918 digits += 1;
919 result = result.mul_wrapping(base);
920 result = result.add_wrapping(T::Native::usize_as((b - b'0') as usize));
921 }
922 b'.' => {
923 let point_index = index;
924
925 for (_, b) in bs.by_ref() {
926 if !b.is_ascii_digit() {
927 if *b == b'e' || *b == b'E' {
928 result = parse_e_notation::<T>(
929 s,
930 digits as u16,
931 fractionals as i16,
932 result,

Callers

nothing calls this directly

Calls 7

firstMethod · 0.80
as_bytesMethod · 0.45
lastMethod · 0.45
lenMethod · 0.45
skipMethod · 0.45
iterMethod · 0.45
nextMethod · 0.45

Tested by

no test coverage detected