@@ -35,7 +35,10 @@ use crate::util::bit_util::FromBytes;
35
35
/// The type only takes 12 bytes, without extra padding.
36
36
#[ derive( Clone , Copy , Debug , Default , PartialEq , Eq ) ]
37
37
pub 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 ,
39
42
}
40
43
41
44
const JULIAN_DAY_OF_EPOCH : i64 = 2_440_588 ;
@@ -59,19 +62,25 @@ const NANOSECONDS_IN_DAY: i64 = SECONDS_IN_DAY * NANOSECONDS;
59
62
impl Int96 {
60
63
/// Creates new INT96 type struct with no data set.
61
64
pub fn new ( ) -> Self {
62
- Self { value : [ 0 ; 3 ] }
65
+ Self { nanos : 0 , days : 0 }
63
66
}
64
67
65
- /// Returns underlying data as slice of [`u32`].
68
+ /// Returns underlying data as slice of [`u32`] for compatibility with Parquet format
66
69
#[ inline]
67
70
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 ) }
69
77
}
70
78
71
- /// Sets data for this INT96 type.
79
+ /// Sets data for this INT96 type from raw Parquet format .
72
80
#[ inline]
73
81
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 ;
75
84
}
76
85
77
86
/// Converts this INT96 into an i64 representing the number of MILLISECONDS since Epoch
@@ -124,33 +133,19 @@ impl Int96 {
124
133
. wrapping_add ( nanos)
125
134
}
126
135
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
138
137
#[ 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;
141
141
}
142
142
143
- /// Sets the INT96 data from microseconds since epoch
144
- ///
145
- /// Will wrap around on overflow
146
143
#[ 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 )
149
146
}
150
147
151
148
/// Sets the INT96 data from nanoseconds since epoch
152
- ///
153
- /// Will wrap around on overflow
154
149
#[ inline]
155
150
pub fn set_data_from_nanos ( & mut self , nanos : i64 ) {
156
151
let days = nanos / NANOSECONDS_IN_DAY ;
@@ -159,24 +154,32 @@ impl Int96 {
159
154
self . set_data_from_days_and_nanos ( julian_day, remaining_nanos) ;
160
155
}
161
156
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
+ }
166
162
163
+ /// Sets the INT96 data from milliseconds since epoch
167
164
#[ 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 ) )
173
167
}
174
168
169
+ /// Sets the INT96 data from microseconds since epoch
175
170
#[ 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
180
183
}
181
184
}
182
185
@@ -188,25 +191,13 @@ impl PartialOrd for Int96 {
188
191
189
192
impl Ord for Int96 {
190
193
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 ) ,
196
196
ord => ord,
197
197
}
198
198
}
199
199
}
200
200
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
-
210
201
impl fmt:: Display for Int96 {
211
202
#[ cold]
212
203
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
@@ -675,8 +666,8 @@ impl AsBytes for bool {
675
666
676
667
impl AsBytes for Int96 {
677
668
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 ) }
680
671
}
681
672
}
682
673
0 commit comments