in native/spark-expr/src/conversion_funcs/cast.rs [1604:1693]
fn do_cast_string_to_int<
T: Num + PartialOrd + Integer + CheckedSub + CheckedNeg + From<i32> + Copy,
>(
str: &str,
eval_mode: EvalMode,
type_name: &str,
min_value: T,
) -> SparkResult<Option<T>> {
let trimmed_str = str.trim();
if trimmed_str.is_empty() {
return none_or_err(eval_mode, type_name, str);
}
let len = trimmed_str.len();
let mut result: T = T::zero();
let mut negative = false;
let radix = T::from(10);
let stop_value = min_value / radix;
let mut parse_sign_and_digits = true;
for (i, ch) in trimmed_str.char_indices() {
if parse_sign_and_digits {
if i == 0 {
negative = ch == '-';
let positive = ch == '+';
if negative || positive {
if i + 1 == len {
// input string is just "+" or "-"
return none_or_err(eval_mode, type_name, str);
}
// consume this char
continue;
}
}
if ch == '.' {
if eval_mode == EvalMode::Legacy {
// truncate decimal in legacy mode
parse_sign_and_digits = false;
continue;
} else {
return none_or_err(eval_mode, type_name, str);
}
}
let digit = if ch.is_ascii_digit() {
(ch as u32) - ('0' as u32)
} else {
return none_or_err(eval_mode, type_name, str);
};
// We are going to process the new digit and accumulate the result. However, before
// doing this, if the result is already smaller than the
// stopValue(Integer.MIN_VALUE / radix), then result * 10 will definitely be
// smaller than minValue, and we can stop
if result < stop_value {
return none_or_err(eval_mode, type_name, str);
}
// Since the previous result is greater than or equal to stopValue(Integer.MIN_VALUE /
// radix), we can just use `result > 0` to check overflow. If result
// overflows, we should stop
let v = result * radix;
let digit = (digit as i32).into();
match v.checked_sub(&digit) {
Some(x) if x <= T::zero() => result = x,
_ => {
return none_or_err(eval_mode, type_name, str);
}
}
} else {
// make sure fractional digits are valid digits but ignore them
if !ch.is_ascii_digit() {
return none_or_err(eval_mode, type_name, str);
}
}
}
if !negative {
if let Some(neg) = result.checked_neg() {
if neg < T::zero() {
return none_or_err(eval_mode, type_name, str);
}
result = neg;
} else {
return none_or_err(eval_mode, type_name, str);
}
}
Ok(Some(result))
}