11// SPDX-License-Identifier: Apache-2.0
22// SPDX-FileCopyrightText: Copyright the Vortex contributors
33
4+ use std:: mem:: MaybeUninit ;
5+
46use vortex_array:: ArrayRef ;
57use vortex_array:: ExecutionCtx ;
68use vortex_array:: IntoArray ;
9+ use vortex_array:: arrays:: ConstantArray ;
710use vortex_array:: arrays:: PrimitiveArray ;
811use vortex_array:: arrays:: TemporalArray ;
912use vortex_array:: builtins:: ArrayBuiltins ;
1013use vortex_array:: dtype:: DType ;
1114use vortex_array:: dtype:: PType ;
15+ use vortex_array:: extension:: datetime:: TimeUnit ;
1216use vortex_buffer:: BufferMut ;
1317use vortex_error:: VortexResult ;
18+ use vortex_error:: vortex_bail;
1419
1520use crate :: timestamp;
21+ /// All parts are stored as i32. seconds and subseconds always fit in i32.
22+ /// For days we have a upper day limitation due to "jiff" validation so days
23+ /// also fit in i32.
1624pub struct TemporalParts {
1725 pub days : ArrayRef ,
1826 pub seconds : ArrayRef ,
@@ -39,25 +47,98 @@ pub fn split_temporal(array: TemporalArray, ctx: &mut ExecutionCtx) -> VortexRes
3947 ) ) ?
4048 . execute :: < PrimitiveArray > ( ctx) ?;
4149
50+ let time_unit = array. temporal_metadata ( ) . time_unit ( ) ;
51+ if matches ! ( time_unit, TimeUnit :: Days ) {
52+ vortex_bail ! ( "Cannot handle day-level data" ) ;
53+ }
54+
4255 let length = timestamps. len ( ) ;
43- let mut days = BufferMut :: with_capacity ( length) ;
44- let mut seconds = BufferMut :: with_capacity ( length) ;
45- let mut subseconds = BufferMut :: with_capacity ( length) ;
46-
47- for & ts in timestamps. as_slice :: < i64 > ( ) {
48- let ts_parts = timestamp:: split ( ts, array. temporal_metadata ( ) . time_unit ( ) ) ?;
49- days. push ( ts_parts. days ) ;
50- seconds. push ( ts_parts. seconds ) ;
51- subseconds. push ( ts_parts. subseconds ) ;
56+
57+ // If we don't [..length], compiler can't infer all 3 or 4 slices are the
58+ // same length, and zip() in split_slice_* checks iterator boundaries.
59+ let timestamps = & timestamps. as_slice :: < i64 > ( ) [ ..length] ;
60+
61+ let mut days: BufferMut < i32 > = BufferMut :: with_capacity ( timestamps. len ( ) ) ;
62+ let mut seconds: BufferMut < i32 > = BufferMut :: with_capacity ( timestamps. len ( ) ) ;
63+ let days_ptr = & mut days. spare_capacity_mut ( ) [ ..length] ;
64+ let seconds_ptr = & mut seconds. spare_capacity_mut ( ) [ ..length] ;
65+
66+ if matches ! ( time_unit, TimeUnit :: Seconds ) {
67+ split_slice_seconds ( days_ptr, seconds_ptr, timestamps) ;
68+
69+ // SAFETY: all items in [0; length) are filled in split_slice_seconds
70+ unsafe {
71+ days. set_len ( length) ;
72+ seconds. set_len ( length) ;
73+ }
74+
75+ return Ok ( TemporalParts {
76+ days : PrimitiveArray :: new ( days. freeze ( ) , temporal_values. validity ( ) ?) . into_array ( ) ,
77+ seconds : seconds. into_array ( ) ,
78+ subseconds : ConstantArray :: new ( 0 , length) . into_array ( ) ,
79+ } ) ;
80+ }
81+
82+ let mut subseconds: BufferMut < i32 > = BufferMut :: with_capacity ( timestamps. len ( ) ) ;
83+ let subseconds_ptr = & mut subseconds. spare_capacity_mut ( ) [ ..length] ;
84+
85+ match time_unit {
86+ TimeUnit :: Nanoseconds => {
87+ split_slice :: < 1_000_000_000 > ( days_ptr, seconds_ptr, subseconds_ptr, timestamps)
88+ }
89+ TimeUnit :: Microseconds => {
90+ split_slice :: < 1_000_000 > ( days_ptr, seconds_ptr, subseconds_ptr, timestamps)
91+ }
92+ TimeUnit :: Milliseconds => {
93+ split_slice :: < 1_000 > ( days_ptr, seconds_ptr, subseconds_ptr, timestamps)
94+ }
95+ _ => unreachable ! ( "Handled before" ) ,
96+ } ;
97+
98+ // SAFETY: all items in [0; length) are filled in split_slice
99+ unsafe {
100+ days. set_len ( length) ;
101+ seconds. set_len ( length) ;
102+ subseconds. set_len ( length) ;
52103 }
53104
54105 Ok ( TemporalParts {
55- days : PrimitiveArray :: new ( days, temporal_values. validity ( ) ?) . into_array ( ) ,
106+ days : PrimitiveArray :: new ( days. freeze ( ) , temporal_values. validity ( ) ?) . into_array ( ) ,
56107 seconds : seconds. into_array ( ) ,
57108 subseconds : subseconds. into_array ( ) ,
58109 } )
59110}
60111
112+ #[ inline]
113+ fn split_slice < const DIVISOR : i64 > (
114+ days : & mut [ MaybeUninit < i32 > ] ,
115+ seconds : & mut [ MaybeUninit < i32 > ] ,
116+ subseconds : & mut [ MaybeUninit < i32 > ] ,
117+ timestamps : & [ i64 ] ,
118+ ) {
119+ for ( ( ( day, second) , subseconds) , ts) in
120+ days. iter_mut ( ) . zip ( seconds) . zip ( subseconds) . zip ( timestamps)
121+ {
122+ let parts = timestamp:: split_with_divisor :: < DIVISOR > ( * ts) ;
123+ day. write ( parts. days ) ;
124+ second. write ( parts. seconds ) ;
125+ subseconds. write ( parts. subseconds ) ;
126+ }
127+ }
128+
129+ #[ inline]
130+ fn split_slice_seconds (
131+ days : & mut [ MaybeUninit < i32 > ] ,
132+ seconds : & mut [ MaybeUninit < i32 > ] ,
133+ timestamps : & [ i64 ] ,
134+ ) {
135+ for ( ( day, second) , ts) in days. iter_mut ( ) . zip ( seconds) . zip ( timestamps) {
136+ let parts = timestamp:: split_with_divisor :: < 1 > ( * ts) ;
137+ day. write ( parts. days ) ;
138+ second. write ( parts. seconds ) ;
139+ }
140+ }
141+
61142#[ cfg( test) ]
62143mod tests {
63144 use rstest:: rstest;
0 commit comments