String characters are variable length encoded in UTF-8, `substr()` function's arguments are character-based, converting them into byte-based indices requires expensive decoding. However, checking if a string is ASCII-only is relatively cheap. If strings are ASCII only, use byte-based indices instead. A common pattern to call `substr()` is taking a small prefix of a long string, such as `substr(lo
(
string_array: &V,
start: &Int64Array,
count: Option<&Int64Array>,
)
| 232 | // In such case the overhead of ASCII-validation may not be worth it, so |
| 233 | // skip the validation for short prefix for now. |
| 234 | pub fn enable_ascii_fast_path<'a, V: StringArrayType<'a>>( |
| 235 | string_array: &V, |
| 236 | start: &Int64Array, |
| 237 | count: Option<&Int64Array>, |
| 238 | ) -> bool { |
| 239 | let is_short_prefix = match count { |
| 240 | Some(count) => { |
| 241 | let short_prefix_threshold = 32.0; |
| 242 | let n_sample = 10; |
| 243 | |
| 244 | // HACK: can be simplified if function has specialized |
| 245 | // implementation for `ScalarValue` (implement without `make_scalar_function()`) |
| 246 | let total_prefix_len = start |
| 247 | .iter() |
| 248 | .zip(count.iter()) |
| 249 | .take(n_sample) |
| 250 | .map(|(start, count)| { |
| 251 | let start = start.unwrap_or(0); |
| 252 | let count = count.unwrap_or(0); |
| 253 | // To get substring, need to decode from 0 to start+count instead of start to start+count |
| 254 | start.saturating_add(count) |
| 255 | }) |
| 256 | .fold(0i64, |acc, val| acc.saturating_add(val)); |
| 257 | |
| 258 | (total_prefix_len as f64 / n_sample as f64) <= short_prefix_threshold |
| 259 | } |
| 260 | None => false, |
| 261 | }; |
| 262 | |
| 263 | if is_short_prefix { |
| 264 | // Skip ASCII validation for short prefix |
| 265 | false |
| 266 | } else { |
| 267 | string_array.is_ascii() |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | fn string_view_substr( |
| 272 | string_view_array: &StringViewArray, |
no test coverage detected
searching dependent graphs…