@@ -50,8 +50,10 @@ struct BandRefImpl<'a> {
5050 data_type : BandDataType ,
5151 /// Per-visible-axis view, length = ndim
5252 view_entries : Vec < ViewEntry > ,
53- /// Visible shape (== `[v.steps for v in view_entries]`), length = ndim
54- visible_shape : Vec < u64 > ,
53+ /// Visible shape (== `[v.steps for v in view_entries]`), length = ndim.
54+ /// `i64` to match the surrounding view-machinery arithmetic
55+ /// (strides, offsets); `validate_view` guarantees `>= 0`.
56+ visible_shape : Vec < i64 > ,
5557 /// Byte strides per visible axis. May be 0 (broadcast) or negative.
5658 byte_strides : Vec < i64 > ,
5759 /// Byte offset into `data` of the visible region's `[0,...,0]` element.
@@ -84,7 +86,18 @@ impl<'a> BandRef for BandRefImpl<'a> {
8486 }
8587
8688 fn shape ( & self ) -> & [ u64 ] {
87- & self . visible_shape
89+ // SAFETY: `visible_shape` elements are i64 but `validate_view`
90+ // guarantees they are all `>= 0` at construction. i64 and u64
91+ // have identical bit layout for non-negative values, so
92+ // reinterpreting the slice is sound under that invariant. The
93+ // alternative (storing a parallel `Vec<u64>` or allocating on
94+ // every call) is wasteful for a hot accessor.
95+ unsafe {
96+ std:: slice:: from_raw_parts (
97+ self . visible_shape . as_ptr ( ) as * const u64 ,
98+ self . visible_shape . len ( ) ,
99+ )
100+ }
88101 }
89102
90103 fn raw_source_shape ( & self ) -> & [ u64 ] {
@@ -168,13 +181,11 @@ impl<'a> BandRef for BandRefImpl<'a> {
168181 }
169182 // shape and strides are owned by NdBuffer (see its doc comment).
170183 // Cloning here is cheap — both vecs are O(ndim), a handful of values.
171- // Cast offset i64 -> u64: safe because RasterRefImpl::band asserts
172- // byte_offset >= 0 before storing.
173184 Ok ( NdBuffer {
174185 buffer : self . data_array . value ( self . band_row ) ,
175186 shape : self . visible_shape . clone ( ) ,
176187 strides : self . byte_strides . clone ( ) ,
177- offset : self . byte_offset as u64 ,
188+ offset : self . byte_offset ,
178189 data_type : self . data_type ,
179190 } )
180191 }
@@ -186,10 +197,8 @@ impl<'a> BandRef for BandRefImpl<'a> {
186197 // ARE the visible bytes. Borrow them.
187198 return Ok ( Cow :: Borrowed ( buf. buffer ) ) ;
188199 }
189- // Use self.* layout fields rather than `buf.*` to avoid the
190- // i64 -> u64 -> i64 round-trip through NdBuffer for `byte_offset`.
191- // The visible shape and byte strides are precomputed once at
192- // construction; `buf.shape` / `buf.strides` are clones of those.
200+ // Use self.* layout fields directly; `buf.shape` / `buf.strides`
201+ // are clones of these same fields.
193202 let out = materialize_strided (
194203 buf. buffer ,
195204 & self . visible_shape ,
@@ -220,7 +229,7 @@ impl<'a> BandRef for BandRefImpl<'a> {
220229/// and skip the check.
221230fn check_view_buffer_bounds (
222231 buffer_len : usize ,
223- visible_shape : & [ u64 ] ,
232+ visible_shape : & [ i64 ] ,
224233 byte_strides : & [ i64 ] ,
225234 byte_offset : i64 ,
226235 dtype_size : usize ,
@@ -231,9 +240,9 @@ fn check_view_buffer_bounds(
231240 let mut min_offset = byte_offset;
232241 let mut max_offset = byte_offset;
233242 for ( k, & stride) in byte_strides. iter ( ) . enumerate ( ) {
234- let last_idx = i64 :: try_from ( visible_shape[ k] - 1 ) . map_err ( |_| {
235- ArrowError :: InvalidArgumentError ( format ! ( " visible_shape[{k} ] - 1 exceeds i64::MAX" ) )
236- } ) ? ;
243+ // `validate_view` guarantees `steps >= 0`, so ` visible_shape[k] >= 0`
244+ // and ` visible_shape[k ] - 1` is in-range for any non-empty axis.
245+ let last_idx = visible_shape [ k ] - 1 ;
237246 let contribution = last_idx. checked_mul ( stride) . ok_or_else ( || {
238247 ArrowError :: InvalidArgumentError ( format ! (
239248 "max addressable offset on axis {k} overflows i64"
@@ -285,7 +294,7 @@ fn check_view_buffer_bounds(
285294/// surface the failure.
286295fn materialize_strided (
287296 buffer : & [ u8 ] ,
288- visible_shape : & [ u64 ] ,
297+ visible_shape : & [ i64 ] ,
289298 byte_strides : & [ i64 ] ,
290299 byte_offset : i64 ,
291300 dtype_size : usize ,
@@ -302,7 +311,7 @@ fn materialize_strided(
302311 // we'd otherwise repeat here, so this walk uses plain arithmetic and
303312 // unchecked slice indexing within the buffer.
304313 let ndim = visible_shape. len ( ) ;
305- let total: u64 = visible_shape. iter ( ) . product ( ) ;
314+ let total: i64 = visible_shape. iter ( ) . product ( ) ;
306315 if total == 0 {
307316 return Vec :: new ( ) ;
308317 }
@@ -322,13 +331,13 @@ fn materialize_strided(
322331
323332 // Precompute a small index vector for outer axes (everything except
324333 // the innermost). For 1D this is empty and we run a single pass.
325- let mut outer_idx = vec ! [ 0u64 ; ndim. saturating_sub( 1 ) ] ;
334+ let mut outer_idx = vec ! [ 0i64 ; ndim. saturating_sub( 1 ) ] ;
326335 loop {
327336 // Compute the byte offset of the row's first element from the
328337 // current outer index combination.
329338 let mut row_off = base;
330339 for ( k, & i) in outer_idx. iter ( ) . enumerate ( ) {
331- row_off += ( i as i64 ) * byte_strides[ k] ;
340+ row_off += i * byte_strides[ k] ;
332341 }
333342
334343 if row_bytes_contiguous {
@@ -435,18 +444,37 @@ impl<'a> RasterRef for RasterRefImpl<'a> {
435444 // Read source shape slice.
436445 let ss_start = self . band_source_shape_list . value_offsets ( ) [ band_row] as usize ;
437446 let ss_end = self . band_source_shape_list . value_offsets ( ) [ band_row + 1 ] as usize ;
438- let source_shape : & [ u64 ] = & self . band_source_shape_values . values ( ) [ ss_start..ss_end] ;
447+ let source_shape_u64 : & [ u64 ] = & self . band_source_shape_values . values ( ) [ ss_start..ss_end] ;
439448
440449 // Reject 0-D bands at the read boundary. Schema doesn't forbid them
441450 // outright but every consumer assumes ndim >= 1.
442- if source_shape . is_empty ( ) {
451+ if source_shape_u64 . is_empty ( ) {
443452 return Err ( ArrowError :: ExternalError ( Box :: new (
444453 sedona_common:: sedona_internal_datafusion_err!(
445454 "band {band_row} has empty source_shape; ndim must be >= 1"
446455 ) ,
447456 ) ) ) ;
448457 }
449458
459+ // Convert source_shape u64 → i64 once with overflow check. Every
460+ // downstream consumer in the view machinery wants i64 (matches
461+ // ViewEntry's signed fields and the stride arithmetic). The cast
462+ // is fallible only on cosmically large dims (> 2^63); a clean
463+ // internal error is better than a wrap that silently passes
464+ // later bound checks.
465+ let source_shape: Vec < i64 > = source_shape_u64
466+ . iter ( )
467+ . map ( |& s| {
468+ i64:: try_from ( s) . map_err ( |_| {
469+ ArrowError :: ExternalError ( Box :: new (
470+ sedona_common:: sedona_internal_datafusion_err!(
471+ "band {band_row}: source_shape axis {s} exceeds i64::MAX"
472+ ) ,
473+ ) )
474+ } )
475+ } )
476+ . collect :: < Result < _ , _ > > ( ) ?;
477+
450478 // Resolve data type up front; an unknown discriminant is a
451479 // schema-corruption bug, not user data, so failing the band loudly
452480 // here is appropriate.
@@ -474,7 +502,7 @@ impl<'a> RasterRef for RasterRefImpl<'a> {
474502 source_axis : i as i64 ,
475503 start : 0 ,
476504 step : 1 ,
477- steps : s as i64 ,
505+ steps : s,
478506 } )
479507 . collect ( )
480508 } else {
@@ -492,7 +520,7 @@ impl<'a> RasterRef for RasterRefImpl<'a> {
492520
493521 // Full validation: length match, source_axis permutation, bounds,
494522 // and steps >= 0. Anything malformed is schema-level corruption.
495- if let Err ( e) = validate_view ( & view_entries, source_shape) {
523+ if let Err ( e) = validate_view ( & view_entries, & source_shape) {
496524 return Err ( ArrowError :: ExternalError ( Box :: new (
497525 sedona_common:: sedona_internal_datafusion_err!(
498526 "band {band_row} has malformed view: {e}"
@@ -517,17 +545,14 @@ impl<'a> RasterRef for RasterRefImpl<'a> {
517545 // C-order byte strides over the source_shape:
518546 // source_strides_bytes[k] = dtype_size * Π_{j>k} source_shape[j]
519547 //
520- // Computed with checked arithmetic so a corrupt source_shape (a u64
521- // that doesn't fit in i64, or a product that overflows) is rejected
522- // here rather than producing a wrapped stride that silently passes
523- // later bound checks.
548+ // Computed with checked arithmetic so a corrupt source_shape whose
549+ // product overflows i64 is rejected rather than producing a wrapped
550+ // stride that silently passes later bound checks.
524551 let mut source_strides_bytes = vec ! [ 0i64 ; source_shape. len( ) ] ;
525552 source_strides_bytes[ source_shape. len ( ) - 1 ] = dtype_size;
526553 for k in ( 0 ..source_shape. len ( ) - 1 ) . rev ( ) {
527- let next_axis = i64:: try_from ( source_shape[ k + 1 ] )
528- . map_err ( |_| overflow_err ( "source_shape axis exceeds i64::MAX" ) ) ?;
529554 source_strides_bytes[ k] = source_strides_bytes[ k + 1 ]
530- . checked_mul ( next_axis )
555+ . checked_mul ( source_shape [ k + 1 ] )
531556 . ok_or_else ( || overflow_err ( "source-stride product overflows i64" ) ) ?;
532557 }
533558
@@ -552,7 +577,7 @@ impl<'a> RasterRef for RasterRefImpl<'a> {
552577 . checked_add ( start_off)
553578 . ok_or_else ( || overflow_err ( "view offset accumulation overflows i64" ) ) ?;
554579 }
555- let is_identity_view = is_identity_view ( & view_entries, source_shape) ;
580+ let is_identity_view = is_identity_view ( & view_entries, & source_shape) ;
556581 // byte_offset is non-negative by construction (start >= 0,
557582 // src_stride > 0). Check defensively so a future refactor that
558583 // breaks the invariant surfaces a clean internal error rather than
0 commit comments