@@ -35,7 +35,10 @@ use crate::util::bit_util::FromBytes;
3535/// The type only takes 12 bytes, without extra padding.
3636#[ derive( Clone , Copy , Debug , Default , PartialEq , Eq ) ]
3737pub struct Int96 {
38- value : [ u32 ; 3 ] ,
38+ /// First 8 bytes store nanoseconds since midnight
39+ pub nanos : i64 ,
40+ /// Last 4 bytes store Julian days
41+ pub days : i32 ,
3942}
4043
4144const JULIAN_DAY_OF_EPOCH : i64 = 2_440_588 ;
@@ -59,19 +62,25 @@ const NANOSECONDS_IN_DAY: i64 = SECONDS_IN_DAY * NANOSECONDS;
5962impl Int96 {
6063 /// Creates new INT96 type struct with no data set.
6164 pub fn new ( ) -> Self {
62- Self { value : [ 0 ; 3 ] }
65+ Self { nanos : 0 , days : 0 }
6366 }
6467
65- /// Returns underlying data as slice of [`u32`].
68+ /// Returns underlying data as slice of [`u32`] for compatibility with Parquet format
6669 #[ inline]
6770 pub fn data ( & self ) -> & [ u32 ] {
68- & self . value
71+ // SAFETY: We're reinterpreting the bytes of our struct as [u32; 3]
72+ // This is safe because:
73+ // 1. The memory layout is compatible (12 bytes total)
74+ // 2. The alignment requirements are met (u32 requires 4-byte alignment)
75+ // 3. We maintain the invariant that the bytes are always valid u32s
76+ unsafe { std:: slice:: from_raw_parts ( self as * const Int96 as * const u32 , 3 ) }
6977 }
7078
71- /// Sets data for this INT96 type.
79+ /// Sets data for this INT96 type from raw Parquet format .
7280 #[ inline]
7381 pub fn set_data ( & mut self , elem0 : u32 , elem1 : u32 , elem2 : u32 ) {
74- self . value = [ elem0, elem1, elem2] ;
82+ self . nanos = ( ( elem1 as i64 ) << 32 ) | ( elem0 as i64 ) ;
83+ self . days = elem2 as i32 ;
7584 }
7685
7786 /// Converts this INT96 into an i64 representing the number of MILLISECONDS since Epoch
@@ -124,33 +133,19 @@ impl Int96 {
124133 . wrapping_add ( nanos)
125134 }
126135
127- /// Sets the INT96 data from seconds since epoch
128- ///
129- /// Will wrap around on overflow
130- #[ inline]
131- pub fn set_data_from_seconds ( & mut self , seconds : i64 ) {
132- self . set_data_from_nanos ( seconds. wrapping_mul ( NANOSECONDS ) ) ;
133- }
134-
135- /// Sets the INT96 data from milliseconds since epoch
136- ///
137- /// Will wrap around on overflow
136+ /// Sets the INT96 data directly from days and nanoseconds
138137 #[ inline]
139- pub fn set_data_from_millis ( & mut self , millis : i64 ) {
140- self . set_data_from_nanos ( millis. wrapping_mul ( MICROSECONDS ) ) ;
138+ pub fn set_data_from_days_and_nanos ( & mut self , days : i32 , nanos : i64 ) {
139+ self . days = days;
140+ self . nanos = nanos;
141141 }
142142
143- /// Sets the INT96 data from microseconds since epoch
144- ///
145- /// Will wrap around on overflow
146143 #[ inline]
147- pub fn set_data_from_micros ( & mut self , micros : i64 ) {
148- self . set_data_from_nanos ( micros . wrapping_mul ( MILLISECONDS ) ) ;
144+ fn data_as_days_and_nanos ( & self ) -> ( i32 , i64 ) {
145+ ( self . days , self . nanos )
149146 }
150147
151148 /// Sets the INT96 data from nanoseconds since epoch
152- ///
153- /// Will wrap around on overflow
154149 #[ inline]
155150 pub fn set_data_from_nanos ( & mut self , nanos : i64 ) {
156151 let days = nanos / NANOSECONDS_IN_DAY ;
@@ -159,24 +154,32 @@ impl Int96 {
159154 self . set_data_from_days_and_nanos ( julian_day, remaining_nanos) ;
160155 }
161156
162- /// Sets the INT96 data directly from days and nanoseconds
163- ///
164- /// This is the most direct way to set the Int96 data structure which internally
165- /// stores days and nanoseconds. The days should be Julian days since epoch.
157+ /// Sets the INT96 data from seconds since epoch
158+ #[ inline]
159+ pub fn set_data_from_seconds ( & mut self , seconds : i64 ) {
160+ self . set_data_from_nanos ( seconds. wrapping_mul ( NANOSECONDS ) )
161+ }
166162
163+ /// Sets the INT96 data from milliseconds since epoch
167164 #[ inline]
168- pub fn set_data_from_days_and_nanos ( & mut self , days : i32 , nanos : i64 ) {
169- let julian_day = ( days as i32 ) as u32 ;
170- let nanos_low = ( nanos & 0xFFFFFFFF ) as u32 ;
171- let nanos_high = ( ( nanos >> 32 ) & 0xFFFFFFFF ) as u32 ;
172- self . set_data ( nanos_low, nanos_high, julian_day) ;
165+ pub fn set_data_from_millis ( & mut self , millis : i64 ) {
166+ self . set_data_from_nanos ( millis. wrapping_mul ( MICROSECONDS ) )
173167 }
174168
169+ /// Sets the INT96 data from microseconds since epoch
175170 #[ inline]
176- fn data_as_days_and_nanos ( & self ) -> ( i32 , i64 ) {
177- let day = self . data ( ) [ 2 ] as i32 ;
178- let nanos = ( ( self . data ( ) [ 1 ] as i64 ) << 32 ) + self . data ( ) [ 0 ] as i64 ;
179- ( day, nanos)
171+ pub fn set_data_from_micros ( & mut self , micros : i64 ) {
172+ self . set_data_from_nanos ( micros. wrapping_mul ( MILLISECONDS ) )
173+ }
174+ }
175+
176+
177+ impl From < Vec < u32 > > for Int96 {
178+ fn from ( buf : Vec < u32 > ) -> Self {
179+ assert_eq ! ( buf. len( ) , 3 ) ;
180+ let mut result = Self :: new ( ) ;
181+ result. set_data ( buf[ 0 ] , buf[ 1 ] , buf[ 2 ] ) ;
182+ result
180183 }
181184}
182185
@@ -188,25 +191,13 @@ impl PartialOrd for Int96 {
188191
189192impl Ord for Int96 {
190193 fn cmp ( & self , other : & Self ) -> Ordering {
191- let ( self_days, self_nanos) = self . data_as_days_and_nanos ( ) ;
192- let ( other_days, other_nanos) = other. data_as_days_and_nanos ( ) ;
193-
194- match self_days. cmp ( & other_days) {
195- Ordering :: Equal => self_nanos. cmp ( & other_nanos) ,
194+ match self . days . cmp ( & other. days ) {
195+ Ordering :: Equal => self . nanos . cmp ( & other. nanos ) ,
196196 ord => ord,
197197 }
198198 }
199199}
200200
201- impl From < Vec < u32 > > for Int96 {
202- fn from ( buf : Vec < u32 > ) -> Self {
203- assert_eq ! ( buf. len( ) , 3 ) ;
204- let mut result = Self :: new ( ) ;
205- result. set_data ( buf[ 0 ] , buf[ 1 ] , buf[ 2 ] ) ;
206- result
207- }
208- }
209-
210201impl fmt:: Display for Int96 {
211202 #[ cold]
212203 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
@@ -675,8 +666,8 @@ impl AsBytes for bool {
675666
676667impl AsBytes for Int96 {
677668 fn as_bytes ( & self ) -> & [ u8 ] {
678- // SAFETY: Int96::data is a &[u32; 3].
679- unsafe { std:: slice:: from_raw_parts ( self . data ( ) as * const [ u32 ] as * const u8 , 12 ) }
669+ // SAFETY: The layout of Int96 is i64 followed by i32, which is 12 contiguous bytes
670+ unsafe { std:: slice:: from_raw_parts ( self as * const Int96 as * const u8 , 12 ) }
680671 }
681672}
682673
0 commit comments