From a0b3aafe651efc97329b227c8a2b17e9d21c57cd Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:20:56 +0300 Subject: [PATCH 1/3] chore: use new `OffsetBuffer::subtract` helper --- datafusion/common/src/utils/mod.rs | 11 +---------- datafusion/functions-nested/src/sort.rs | 7 +------ datafusion/functions/src/string/common.rs | 10 +--------- datafusion/functions/src/unicode/initcap.rs | 12 +----------- 4 files changed, 4 insertions(+), 36 deletions(-) diff --git a/datafusion/common/src/utils/mod.rs b/datafusion/common/src/utils/mod.rs index f71cf23d5348b..8b00bbe0abb59 100644 --- a/datafusion/common/src/utils/mod.rs +++ b/datafusion/common/src/utils/mod.rs @@ -1236,16 +1236,7 @@ pub fn adjust_offsets_for_slice( ) -> OffsetBuffer { let offsets = list.offsets(); - if let (Some(first), Some(last)) = (offsets.first(), offsets.last()) - && (!first.is_zero() || last.as_usize() != list.values().len()) - { - let offsets = offsets.iter().map(|offset| *offset - *first).collect(); - - //todo: use unsafe Offset::new_unchecked? - return OffsetBuffer::new(offsets); - } - - offsets.clone() + offsets.clone().subtract(*offsets[0]) } /// For lists and large lists, truncates the sublist of null values diff --git a/datafusion/functions-nested/src/sort.rs b/datafusion/functions-nested/src/sort.rs index 0a34cce6b965f..0734b60b4527c 100644 --- a/datafusion/functions-nested/src/sort.rs +++ b/datafusion/functions-nested/src/sort.rs @@ -471,12 +471,7 @@ fn take_by_indices( fn rebase_offsets( offsets: &OffsetBuffer, ) -> OffsetBuffer { - if offsets[0].as_usize() == 0 { - offsets.clone() - } else { - let rebased: Vec = offsets.iter().map(|o| *o - offsets[0]).collect(); - OffsetBuffer::new(rebased.into()) - } + offsets.clone().subtract(*offsets[0]) } fn order_desc(modifier: &str) -> Result { diff --git a/datafusion/functions/src/string/common.rs b/datafusion/functions/src/string/common.rs index b51b92e9df1ed..59273e4b07066 100644 --- a/datafusion/functions/src/string/common.rs +++ b/datafusion/functions/src/string/common.rs @@ -636,15 +636,7 @@ fn case_conversion_ascii_array( let values = Buffer::from_vec(converted); // Shift offsets from `start`-based to 0-based so they index into `values`. - let offsets = if start == 0 { - string_array.offsets().clone() - } else { - let s = O::usize_as(start); - let rebased: Vec = value_offsets.iter().map(|&o| o - s).collect(); - // SAFETY: subtracting a constant from monotonic offsets preserves - // monotonicity, and `start` is the minimum offset, so no underflow. - unsafe { OffsetBuffer::new_unchecked(ScalarBuffer::from(rebased)) } - }; + let offsets = string_array.offsets().clone().subtract(*string_array.offsets()[0]); let nulls = string_array.nulls().cloned(); // SAFETY: offsets are monotonic and in-bounds for `values`; nulls diff --git a/datafusion/functions/src/unicode/initcap.rs b/datafusion/functions/src/unicode/initcap.rs index 9192f23844f16..56856d4b5e972 100644 --- a/datafusion/functions/src/unicode/initcap.rs +++ b/datafusion/functions/src/unicode/initcap.rs @@ -217,17 +217,7 @@ fn initcap_ascii_array( } let values = Buffer::from_vec(out); - let out_offsets = if first_offset == 0 { - offsets.clone() - } else { - // For sliced arrays, we need to rebase the offsets to reflect that the - // output only contains the bytes in the visible slice. - let rebased_offsets = offsets - .iter() - .map(|offset| T::usize_as(offset.as_usize() - first_offset)) - .collect::>(); - OffsetBuffer::::new(rebased_offsets.into()) - }; + let out_offsets = offsets.clone().subtract(*offsets[0]); // SAFETY: ASCII case conversion preserves byte length, so the original // string boundaries are preserved. `out_offsets` is either identical to From 314fdc3d13b78070bf7ba8f2e80e8d9d8fa725ad Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:28:00 +0300 Subject: [PATCH 2/3] chore: use new `OffsetBuffer::subtract` helper --- datafusion/common/src/utils/mod.rs | 2 +- datafusion/functions-nested/src/sort.rs | 2 +- datafusion/functions/src/string/common.rs | 7 +++++-- datafusion/functions/src/unicode/initcap.rs | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/datafusion/common/src/utils/mod.rs b/datafusion/common/src/utils/mod.rs index 8b00bbe0abb59..94bbb91a7fa8b 100644 --- a/datafusion/common/src/utils/mod.rs +++ b/datafusion/common/src/utils/mod.rs @@ -1236,7 +1236,7 @@ pub fn adjust_offsets_for_slice( ) -> OffsetBuffer { let offsets = list.offsets(); - offsets.clone().subtract(*offsets[0]) + offsets.clone().subtract(offsets[0]) } /// For lists and large lists, truncates the sublist of null values diff --git a/datafusion/functions-nested/src/sort.rs b/datafusion/functions-nested/src/sort.rs index 0734b60b4527c..ca9267bb88c82 100644 --- a/datafusion/functions-nested/src/sort.rs +++ b/datafusion/functions-nested/src/sort.rs @@ -471,7 +471,7 @@ fn take_by_indices( fn rebase_offsets( offsets: &OffsetBuffer, ) -> OffsetBuffer { - offsets.clone().subtract(*offsets[0]) + offsets.clone().subtract(offsets[0]) } fn order_desc(modifier: &str) -> Result { diff --git a/datafusion/functions/src/string/common.rs b/datafusion/functions/src/string/common.rs index 59273e4b07066..7a5d2573ec257 100644 --- a/datafusion/functions/src/string/common.rs +++ b/datafusion/functions/src/string/common.rs @@ -27,7 +27,7 @@ use arrow::array::{ Array, ArrayRef, AsArray, GenericStringArray, NullBufferBuilder, OffsetSizeTrait, StringViewArray, new_null_array, }; -use arrow::buffer::{Buffer, OffsetBuffer, ScalarBuffer}; +use arrow::buffer::{Buffer, ScalarBuffer}; use arrow::datatypes::DataType; use datafusion_common::Result; use datafusion_common::cast::{as_generic_string_array, as_string_view_array}; @@ -636,7 +636,10 @@ fn case_conversion_ascii_array( let values = Buffer::from_vec(converted); // Shift offsets from `start`-based to 0-based so they index into `values`. - let offsets = string_array.offsets().clone().subtract(*string_array.offsets()[0]); + let offsets = string_array + .offsets() + .clone() + .subtract(string_array.offsets()[0]); let nulls = string_array.nulls().cloned(); // SAFETY: offsets are monotonic and in-bounds for `values`; nulls diff --git a/datafusion/functions/src/unicode/initcap.rs b/datafusion/functions/src/unicode/initcap.rs index 56856d4b5e972..51701331d21dd 100644 --- a/datafusion/functions/src/unicode/initcap.rs +++ b/datafusion/functions/src/unicode/initcap.rs @@ -18,7 +18,7 @@ use std::sync::Arc; use arrow::array::{Array, ArrayRef, GenericStringArray, OffsetSizeTrait}; -use arrow::buffer::{Buffer, OffsetBuffer}; +use arrow::buffer::Buffer; use arrow::datatypes::DataType; use crate::strings::{GenericStringArrayBuilder, StringViewArrayBuilder}; @@ -217,7 +217,7 @@ fn initcap_ascii_array( } let values = Buffer::from_vec(out); - let out_offsets = offsets.clone().subtract(*offsets[0]); + let out_offsets = offsets.clone().subtract(offsets[0]); // SAFETY: ASCII case conversion preserves byte length, so the original // string boundaries are preserved. `out_offsets` is either identical to From 51365aeb4723f6b5a746d695a09464c35e2a7f07 Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:32:55 +0300 Subject: [PATCH 3/3] update comment --- datafusion/functions/src/unicode/initcap.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/datafusion/functions/src/unicode/initcap.rs b/datafusion/functions/src/unicode/initcap.rs index 51701331d21dd..8981d59aec8d2 100644 --- a/datafusion/functions/src/unicode/initcap.rs +++ b/datafusion/functions/src/unicode/initcap.rs @@ -217,6 +217,9 @@ fn initcap_ascii_array( } let values = Buffer::from_vec(out); + + // Rebase offsets for sliced arrays to reflect that the + // output only contains the bytes in the visible slice. let out_offsets = offsets.clone().subtract(offsets[0]); // SAFETY: ASCII case conversion preserves byte length, so the original