Skip to content

Commit f1299eb

Browse files
author
B Vadlamani
committed
improve_benchmark_remove_unwanted_branching_per_eval_mode
1 parent 94d0f32 commit f1299eb

1 file changed

Lines changed: 84 additions & 105 deletions

File tree

  • native/spark-expr/src/conversion_funcs

native/spark-expr/src/conversion_funcs/cast.rs

Lines changed: 84 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1964,14 +1964,8 @@ fn cast_string_to_int_with_range_check(
19641964
}
19651965
}
19661966

1967-
/// Equivalent to
1968-
/// - org.apache.spark.unsafe.types.UTF8String.toInt(IntWrapper intWrapper, boolean allowDecimal)
1969-
/// - org.apache.spark.unsafe.types.UTF8String.toLong(LongWrapper longWrapper, boolean allowDecimal)
1970-
fn do_parse_string_to_int_legacy<T: Integer + CheckedSub + CheckedNeg + From<u8> + Copy>(
1971-
str: &str,
1972-
min_value: T,
1973-
) -> SparkResult<Option<T>> {
1974-
let bytes = str.as_bytes();
1967+
// Returns (start, end) indices after trimming whitespace
1968+
fn trim_whitespace(bytes: &[u8]) -> (usize, usize) {
19751969
let mut start = 0;
19761970
let mut end = bytes.len();
19771971

@@ -1982,21 +1976,51 @@ fn do_parse_string_to_int_legacy<T: Integer + CheckedSub + CheckedNeg + From<u8>
19821976
end -= 1;
19831977
}
19841978

1985-
if start == end {
1986-
return Ok(None);
1987-
}
1988-
let trimmed_bytes = &bytes[start..end];
1979+
(start, end)
1980+
}
1981+
1982+
// Parses sign and returns (is_negative, start_idx after sign)
1983+
// Returns None if invalid (e.g., just "+" or "-")
1984+
fn parse_sign(trimmed_bytes: &[u8]) -> Option<(bool, usize)> {
19891985
let len = trimmed_bytes.len();
1990-
let mut result: T = T::zero();
1991-
let mut idx = 0;
1986+
if len == 0 {
1987+
return None;
1988+
}
1989+
19921990
let first_char = trimmed_bytes[0];
19931991
let negative = first_char == b'-';
1992+
19941993
if negative || first_char == b'+' {
1995-
idx = 1;
19961994
if len == 1 {
1997-
return Ok(None);
1995+
return None;
19981996
}
1997+
Some((negative, 1))
1998+
} else {
1999+
Some((false, 0))
19992000
}
2001+
}
2002+
2003+
/// Equivalent to
2004+
/// - org.apache.spark.unsafe.types.UTF8String.toInt(IntWrapper intWrapper, boolean allowDecimal)
2005+
/// - org.apache.spark.unsafe.types.UTF8String.toLong(LongWrapper longWrapper, boolean allowDecimal)
2006+
fn do_parse_string_to_int_legacy<T: Integer + CheckedSub + CheckedNeg + From<u8> + Copy>(
2007+
str: &str,
2008+
min_value: T,
2009+
) -> SparkResult<Option<T>> {
2010+
let bytes = str.as_bytes();
2011+
let (start, end) = trim_whitespace(bytes);
2012+
2013+
if start == end {
2014+
return Ok(None);
2015+
}
2016+
let trimmed_bytes = &bytes[start..end];
2017+
2018+
let (negative, idx) = match parse_sign(trimmed_bytes) {
2019+
Some(result) => result,
2020+
None => return Ok(None),
2021+
};
2022+
2023+
let mut result: T = T::zero();
20002024

20012025
let radix = T::from(10_u8);
20022026
let stop_value = min_value / radix;
@@ -2027,6 +2051,7 @@ fn do_parse_string_to_int_legacy<T: Integer + CheckedSub + CheckedNeg + From<u8>
20272051
}
20282052
}
20292053
} else {
2054+
// in legacy mode we still process chars after the dot and make sure the chars are digits
20302055
if !ch.is_ascii_digit() {
20312056
return Ok(None);
20322057
}
@@ -2053,60 +2078,41 @@ fn do_parse_string_to_int_ansi<T: Integer + CheckedSub + CheckedNeg + From<u8> +
20532078
min_value: T,
20542079
) -> SparkResult<Option<T>> {
20552080
let bytes = str.as_bytes();
2056-
let mut start = 0;
2057-
let mut end = bytes.len();
2058-
2059-
while start < end && bytes[start].is_ascii_whitespace() {
2060-
start += 1;
2061-
}
2062-
while end > start && bytes[end - 1].is_ascii_whitespace() {
2063-
end -= 1;
2064-
}
2081+
let (start, end) = trim_whitespace(bytes);
20652082

20662083
if start == end {
20672084
return Err(invalid_value(str, "STRING", type_name));
20682085
}
20692086
let trimmed_bytes = &bytes[start..end];
2070-
let len = trimmed_bytes.len();
2087+
2088+
let (negative, idx) = match parse_sign(trimmed_bytes) {
2089+
Some(result) => result,
2090+
None => return Err(invalid_value(str, "STRING", type_name)),
2091+
};
2092+
20712093
let mut result: T = T::zero();
2072-
let mut idx = 0;
2073-
let first_char = trimmed_bytes[0];
2074-
let negative = first_char == b'-';
2075-
if negative || first_char == b'+' {
2076-
idx = 1;
2077-
if len == 1 {
2078-
return Err(invalid_value(str, "STRING", type_name));
2079-
}
2080-
}
20812094

20822095
let radix = T::from(10_u8);
20832096
let stop_value = min_value / radix;
2084-
let mut parse_sign_and_digits = true;
20852097

20862098
for &ch in &trimmed_bytes[idx..] {
2087-
if parse_sign_and_digits {
2088-
if ch == b'.' {
2089-
return Err(invalid_value(str, "STRING", type_name));
2090-
}
2099+
if ch == b'.' {
2100+
return Err(invalid_value(str, "STRING", type_name));
2101+
}
20912102

2092-
if !ch.is_ascii_digit() {
2093-
return Err(invalid_value(str, "STRING", type_name));
2094-
}
2103+
if !ch.is_ascii_digit() {
2104+
return Err(invalid_value(str, "STRING", type_name));
2105+
}
20952106

2096-
let digit: T = T::from(ch - b'0');
2107+
let digit: T = T::from(ch - b'0');
20972108

2098-
if result < stop_value {
2099-
return Err(invalid_value(str, "STRING", type_name));
2100-
}
2101-
let v = result * radix;
2102-
match v.checked_sub(&digit) {
2103-
Some(x) if x <= T::zero() => result = x,
2104-
_ => {
2105-
return Err(invalid_value(str, "STRING", type_name));
2106-
}
2107-
}
2108-
} else {
2109-
if !ch.is_ascii_digit() {
2109+
if result < stop_value {
2110+
return Err(invalid_value(str, "STRING", type_name));
2111+
}
2112+
let v = result * radix;
2113+
match v.checked_sub(&digit) {
2114+
Some(x) if x <= T::zero() => result = x,
2115+
_ => {
21102116
return Err(invalid_value(str, "STRING", type_name));
21112117
}
21122118
}
@@ -2131,60 +2137,42 @@ fn do_parse_string_to_int_try<T: Integer + CheckedSub + CheckedNeg + From<u8> +
21312137
min_value: T,
21322138
) -> SparkResult<Option<T>> {
21332139
let bytes = str.as_bytes();
2134-
let mut start = 0;
2135-
let mut end = bytes.len();
2136-
2137-
while start < end && bytes[start].is_ascii_whitespace() {
2138-
start += 1;
2139-
}
2140-
while end > start && bytes[end - 1].is_ascii_whitespace() {
2141-
end -= 1;
2142-
}
2140+
let (start, end) = trim_whitespace(bytes);
21432141

21442142
if start == end {
21452143
return Ok(None);
21462144
}
21472145
let trimmed_bytes = &bytes[start..end];
2148-
let len = trimmed_bytes.len();
2146+
2147+
let (negative, idx) = match parse_sign(trimmed_bytes) {
2148+
Some(result) => result,
2149+
None => return Ok(None),
2150+
};
2151+
21492152
let mut result: T = T::zero();
2150-
let mut idx = 0;
2151-
let first_char = trimmed_bytes[0];
2152-
let negative = first_char == b'-';
2153-
if negative || first_char == b'+' {
2154-
idx = 1;
2155-
if len == 1 {
2156-
return Ok(None);
2157-
}
2158-
}
21592153

21602154
let radix = T::from(10_u8);
21612155
let stop_value = min_value / radix;
2162-
let mut parse_sign_and_digits = true;
21632156

2157+
// we don't have to go beyond decimal point in try eval mode - early return NULL
21642158
for &ch in &trimmed_bytes[idx..] {
2165-
if parse_sign_and_digits {
2166-
if ch == b'.' {
2167-
return Ok(None);
2168-
}
2159+
if ch == b'.' {
2160+
return Ok(None);
2161+
}
21692162

2170-
if !ch.is_ascii_digit() {
2171-
return Ok(None);
2172-
}
2163+
if !ch.is_ascii_digit() {
2164+
return Ok(None);
2165+
}
21732166

2174-
let digit: T = T::from(ch - b'0');
2167+
let digit: T = T::from(ch - b'0');
21752168

2176-
if result < stop_value {
2177-
return Ok(None);
2178-
}
2179-
let v = result * radix;
2180-
match v.checked_sub(&digit) {
2181-
Some(x) if x <= T::zero() => result = x,
2182-
_ => {
2183-
return Ok(None);
2184-
}
2185-
}
2186-
} else {
2187-
if !ch.is_ascii_digit() {
2169+
if result < stop_value {
2170+
return Ok(None);
2171+
}
2172+
let v = result * radix;
2173+
match v.checked_sub(&digit) {
2174+
Some(x) if x <= T::zero() => result = x,
2175+
_ => {
21882176
return Ok(None);
21892177
}
21902178
}
@@ -2517,15 +2505,6 @@ fn parse_decimal_str(s: &str) -> Result<(i128, i32), String> {
25172505
Ok((final_mantissa, final_scale))
25182506
}
25192507

2520-
/// Either return Ok(None) or Err(SparkError::CastInvalidValue) depending on the evaluation mode
2521-
#[inline]
2522-
fn none_or_err<T>(eval_mode: EvalMode, type_name: &str, str: &str) -> SparkResult<Option<T>> {
2523-
match eval_mode {
2524-
EvalMode::Ansi => Err(invalid_value(str, "STRING", type_name)),
2525-
_ => Ok(None),
2526-
}
2527-
}
2528-
25292508
#[inline]
25302509
fn invalid_value(value: &str, from_type: &str, to_type: &str) -> SparkError {
25312510
SparkError::CastInvalidValue {

0 commit comments

Comments
 (0)