@@ -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
@@ -81,8 +90,6 @@ impl Int96 {
81
90
}
82
91
83
92
/// Converts this INT96 into an i64 representing the number of SECONDS since EPOCH
84
- ///
85
- /// Will wrap around on overflow
86
93
#[ inline]
87
94
pub fn to_seconds ( & self ) -> i64 {
88
95
let ( day, nanos) = self . data_as_days_and_nanos ( ) ;
@@ -92,8 +99,6 @@ impl Int96 {
92
99
}
93
100
94
101
/// Converts this INT96 into an i64 representing the number of MILLISECONDS since EPOCH
95
- ///
96
- /// Will wrap around on overflow
97
102
#[ inline]
98
103
pub fn to_millis ( & self ) -> i64 {
99
104
let ( day, nanos) = self . data_as_days_and_nanos ( ) ;
@@ -103,8 +108,6 @@ impl Int96 {
103
108
}
104
109
105
110
/// Converts this INT96 into an i64 representing the number of MICROSECONDS since EPOCH
106
- ///
107
- /// Will wrap around on overflow
108
111
#[ inline]
109
112
pub fn to_micros ( & self ) -> i64 {
110
113
let ( day, nanos) = self . data_as_days_and_nanos ( ) ;
@@ -114,8 +117,6 @@ impl Int96 {
114
117
}
115
118
116
119
/// Converts this INT96 into an i64 representing the number of NANOSECONDS since EPOCH
117
- ///
118
- /// Will wrap around on overflow
119
120
#[ inline]
120
121
pub fn to_nanos ( & self ) -> i64 {
121
122
let ( day, nanos) = self . data_as_days_and_nanos ( ) ;
@@ -124,33 +125,19 @@ impl Int96 {
124
125
. wrapping_add ( nanos)
125
126
}
126
127
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
128
+ /// Sets the INT96 data directly from days and nanoseconds
138
129
#[ inline]
139
- pub fn set_data_from_millis ( & mut self , millis : i64 ) {
140
- self . set_data_from_nanos ( millis. wrapping_mul ( MICROSECONDS ) ) ;
130
+ pub fn set_data_from_days_and_nanos ( & mut self , days : i32 , nanos : i64 ) {
131
+ self . days = days;
132
+ self . nanos = nanos;
141
133
}
142
134
143
- /// Sets the INT96 data from microseconds since epoch
144
- ///
145
- /// Will wrap around on overflow
146
135
#[ inline]
147
- pub fn set_data_from_micros ( & mut self , micros : i64 ) {
148
- self . set_data_from_nanos ( micros . wrapping_mul ( MILLISECONDS ) ) ;
136
+ fn data_as_days_and_nanos ( & self ) -> ( i32 , i64 ) {
137
+ ( self . days , self . nanos )
149
138
}
150
139
151
140
/// Sets the INT96 data from nanoseconds since epoch
152
- ///
153
- /// Will wrap around on overflow
154
141
#[ inline]
155
142
pub fn set_data_from_nanos ( & mut self , nanos : i64 ) {
156
143
let days = nanos / NANOSECONDS_IN_DAY ;
@@ -159,24 +146,32 @@ impl Int96 {
159
146
self . set_data_from_days_and_nanos ( julian_day, remaining_nanos) ;
160
147
}
161
148
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.
149
+ /// Sets the INT96 data from seconds since epoch
150
+ #[ inline]
151
+ pub fn set_data_from_seconds ( & mut self , seconds : i64 ) {
152
+ self . set_data_from_nanos ( seconds. wrapping_mul ( NANOSECONDS ) )
153
+ }
166
154
155
+ /// Sets the INT96 data from milliseconds since epoch
167
156
#[ 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) ;
157
+ pub fn set_data_from_millis ( & mut self , millis : i64 ) {
158
+ self . set_data_from_nanos ( millis. wrapping_mul ( MICROSECONDS ) )
173
159
}
174
160
161
+ /// Sets the INT96 data from microseconds since epoch
175
162
#[ 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)
163
+ pub fn set_data_from_micros ( & mut self , micros : i64 ) {
164
+ self . set_data_from_nanos ( micros. wrapping_mul ( MILLISECONDS ) )
165
+ }
166
+ }
167
+
168
+
169
+ impl From < Vec < u32 > > for Int96 {
170
+ fn from ( buf : Vec < u32 > ) -> Self {
171
+ assert_eq ! ( buf. len( ) , 3 ) ;
172
+ let mut result = Self :: new ( ) ;
173
+ result. set_data ( buf[ 0 ] , buf[ 1 ] , buf[ 2 ] ) ;
174
+ result
180
175
}
181
176
}
182
177
@@ -188,25 +183,13 @@ impl PartialOrd for Int96 {
188
183
189
184
impl Ord for Int96 {
190
185
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) ,
186
+ match self . days . cmp ( & other. days ) {
187
+ Ordering :: Equal => self . nanos . cmp ( & other. nanos ) ,
196
188
ord => ord,
197
189
}
198
190
}
199
191
}
200
192
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
193
impl fmt:: Display for Int96 {
211
194
#[ cold]
212
195
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
@@ -675,8 +658,8 @@ impl AsBytes for bool {
675
658
676
659
impl AsBytes for Int96 {
677
660
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 ) }
661
+ // SAFETY: The layout of Int96 is i64 followed by i32, which is 12 contiguous bytes
662
+ unsafe { std:: slice:: from_raw_parts ( self as * const Int96 as * const u8 , 12 ) }
680
663
}
681
664
}
682
665
0 commit comments