/Users/andrewlamb/Software/arrow-rs/arrow-buffer/src/native.rs
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | use crate::{i256, IntervalDayTime, IntervalMonthDayNano}; |
19 | | use half::f16; |
20 | | |
21 | | mod private { |
22 | | pub trait Sealed {} |
23 | | } |
24 | | |
25 | | /// Trait expressing a Rust type that has the same in-memory representation as |
26 | | /// Arrow. |
27 | | /// |
28 | | /// This includes `i16`, `f32`, but excludes `bool` (which in arrow is |
29 | | /// represented in bits). |
30 | | /// |
31 | | /// In little endian machines, types that implement [`ArrowNativeType`] can be |
32 | | /// memcopied to arrow buffers as is. |
33 | | /// |
34 | | /// # Transmute Safety |
35 | | /// |
36 | | /// A type T implementing this trait means that any arbitrary slice of bytes of length and |
37 | | /// alignment `size_of::<T>()` can be safely interpreted as a value of that type without |
38 | | /// being unsound, i.e. potentially resulting in undefined behaviour. |
39 | | /// |
40 | | /// Note: in the case of floating point numbers this transmutation can result in a signalling |
41 | | /// NaN, which, whilst sound, can be unwieldy. In general, whilst it is perfectly sound to |
42 | | /// reinterpret bytes as different types using this trait, it is likely unwise. For more information |
43 | | /// see [f32::from_bits] and [f64::from_bits]. |
44 | | /// |
45 | | /// Note: `bool` is restricted to `0` or `1`, and so `bool: !ArrowNativeType` |
46 | | /// |
47 | | /// # Sealed |
48 | | /// |
49 | | /// Due to the above restrictions, this trait is sealed to prevent accidental misuse |
50 | | pub trait ArrowNativeType: |
51 | | std::fmt::Debug + Send + Sync + Copy + PartialOrd + Default + private::Sealed + 'static |
52 | | { |
53 | | /// Returns the byte width of this native type. |
54 | 0 | fn get_byte_width() -> usize { |
55 | 0 | std::mem::size_of::<Self>() |
56 | 0 | } |
57 | | |
58 | | /// Convert native integer type from usize |
59 | | /// |
60 | | /// Returns `None` if [`Self`] is not an integer or conversion would result |
61 | | /// in truncation/overflow |
62 | | fn from_usize(_: usize) -> Option<Self>; |
63 | | |
64 | | /// Convert to usize according to the [`as`] operator |
65 | | /// |
66 | | /// [`as`]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast |
67 | | fn as_usize(self) -> usize; |
68 | | |
69 | | /// Convert from usize according to the [`as`] operator |
70 | | /// |
71 | | /// [`as`]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast |
72 | | fn usize_as(i: usize) -> Self; |
73 | | |
74 | | /// Convert native type to usize. |
75 | | /// |
76 | | /// Returns `None` if [`Self`] is not an integer or conversion would result |
77 | | /// in truncation/overflow |
78 | | fn to_usize(self) -> Option<usize>; |
79 | | |
80 | | /// Convert native type to isize. |
81 | | /// |
82 | | /// Returns `None` if [`Self`] is not an integer or conversion would result |
83 | | /// in truncation/overflow |
84 | | fn to_isize(self) -> Option<isize>; |
85 | | |
86 | | /// Convert native type to i64. |
87 | | /// |
88 | | /// Returns `None` if [`Self`] is not an integer or conversion would result |
89 | | /// in truncation/overflow |
90 | | fn to_i64(self) -> Option<i64>; |
91 | | } |
92 | | |
93 | | macro_rules! native_integer { |
94 | | ($t: ty $(, $from:ident)*) => { |
95 | | impl private::Sealed for $t {} |
96 | | impl ArrowNativeType for $t { |
97 | | #[inline] |
98 | 498 | fn from_usize(v: usize) -> Option<Self> { |
99 | 498 | v.try_into().ok() |
100 | 498 | } |
101 | | |
102 | | #[inline] |
103 | 1.27k | fn to_usize(self) -> Option<usize> { |
104 | 1.27k | self.try_into().ok() |
105 | 1.27k | } |
106 | | |
107 | | #[inline] |
108 | 134 | fn to_isize(self) -> Option<isize> { |
109 | 134 | self.try_into().ok() |
110 | 134 | } |
111 | | |
112 | | #[inline] |
113 | 0 | fn to_i64(self) -> Option<i64> { |
114 | 0 | self.try_into().ok() |
115 | 0 | } |
116 | | |
117 | | #[inline] |
118 | 1.26k | fn as_usize(self) -> usize { |
119 | 1.26k | self as _ |
120 | 1.26k | } |
121 | | |
122 | | #[inline] |
123 | 1.89k | fn usize_as(i: usize) -> Self { |
124 | 1.89k | i as _ |
125 | 1.89k | } |
126 | | } |
127 | | }; |
128 | | } |
129 | | |
130 | | native_integer!(i8); |
131 | | native_integer!(i16); |
132 | | native_integer!(i32); |
133 | | native_integer!(i64); |
134 | | native_integer!(i128); |
135 | | native_integer!(u8); |
136 | | native_integer!(u16); |
137 | | native_integer!(u32); |
138 | | native_integer!(u64); |
139 | | native_integer!(u128); |
140 | | |
141 | | macro_rules! native_float { |
142 | | ($t:ty, $s:ident, $as_usize: expr, $i:ident, $usize_as: expr) => { |
143 | | impl private::Sealed for $t {} |
144 | | impl ArrowNativeType for $t { |
145 | | #[inline] |
146 | | fn from_usize(_: usize) -> Option<Self> { |
147 | | None |
148 | | } |
149 | | |
150 | | #[inline] |
151 | | fn to_usize(self) -> Option<usize> { |
152 | | None |
153 | | } |
154 | | |
155 | | #[inline] |
156 | | fn to_isize(self) -> Option<isize> { |
157 | | None |
158 | | } |
159 | | |
160 | | #[inline] |
161 | 0 | fn to_i64(self) -> Option<i64> { |
162 | 0 | None |
163 | 0 | } |
164 | | |
165 | | #[inline] |
166 | | fn as_usize($s) -> usize { |
167 | | $as_usize |
168 | | } |
169 | | |
170 | | #[inline] |
171 | 0 | fn usize_as($i: usize) -> Self { |
172 | | $usize_as |
173 | 0 | } |
174 | | } |
175 | | }; |
176 | | } |
177 | | |
178 | | native_float!(f16, self, self.to_f32() as _, i, f16::from_f32(i as _)); |
179 | | native_float!(f32, self, self as _, i, i as _); |
180 | | native_float!(f64, self, self as _, i, i as _); |
181 | | |
182 | | impl private::Sealed for i256 {} |
183 | | impl ArrowNativeType for i256 { |
184 | 0 | fn from_usize(u: usize) -> Option<Self> { |
185 | 0 | Some(Self::from_parts(u as u128, 0)) |
186 | 0 | } |
187 | | |
188 | 0 | fn as_usize(self) -> usize { |
189 | 0 | self.to_parts().0 as usize |
190 | 0 | } |
191 | | |
192 | 0 | fn usize_as(i: usize) -> Self { |
193 | 0 | Self::from_parts(i as u128, 0) |
194 | 0 | } |
195 | | |
196 | 0 | fn to_usize(self) -> Option<usize> { |
197 | 0 | let (low, high) = self.to_parts(); |
198 | 0 | if high != 0 { |
199 | 0 | return None; |
200 | 0 | } |
201 | 0 | low.try_into().ok() |
202 | 0 | } |
203 | | |
204 | 0 | fn to_isize(self) -> Option<isize> { |
205 | 0 | self.to_i128()?.try_into().ok() |
206 | 0 | } |
207 | | |
208 | 0 | fn to_i64(self) -> Option<i64> { |
209 | 0 | self.to_i128()?.try_into().ok() |
210 | 0 | } |
211 | | } |
212 | | |
213 | | impl private::Sealed for IntervalMonthDayNano {} |
214 | | impl ArrowNativeType for IntervalMonthDayNano { |
215 | 0 | fn from_usize(_: usize) -> Option<Self> { |
216 | 0 | None |
217 | 0 | } |
218 | | |
219 | 0 | fn as_usize(self) -> usize { |
220 | 0 | ((self.months as u64) | ((self.days as u64) << 32)) as usize |
221 | 0 | } |
222 | | |
223 | 0 | fn usize_as(i: usize) -> Self { |
224 | 0 | Self::new(i as _, ((i as u64) >> 32) as _, 0) |
225 | 0 | } |
226 | | |
227 | 0 | fn to_usize(self) -> Option<usize> { |
228 | 0 | None |
229 | 0 | } |
230 | | |
231 | 0 | fn to_isize(self) -> Option<isize> { |
232 | 0 | None |
233 | 0 | } |
234 | | |
235 | 0 | fn to_i64(self) -> Option<i64> { |
236 | 0 | None |
237 | 0 | } |
238 | | } |
239 | | |
240 | | impl private::Sealed for IntervalDayTime {} |
241 | | impl ArrowNativeType for IntervalDayTime { |
242 | 0 | fn from_usize(_: usize) -> Option<Self> { |
243 | 0 | None |
244 | 0 | } |
245 | | |
246 | 0 | fn as_usize(self) -> usize { |
247 | 0 | ((self.days as u64) | ((self.milliseconds as u64) << 32)) as usize |
248 | 0 | } |
249 | | |
250 | 0 | fn usize_as(i: usize) -> Self { |
251 | 0 | Self::new(i as _, ((i as u64) >> 32) as _) |
252 | 0 | } |
253 | | |
254 | 0 | fn to_usize(self) -> Option<usize> { |
255 | 0 | None |
256 | 0 | } |
257 | | |
258 | 0 | fn to_isize(self) -> Option<isize> { |
259 | 0 | None |
260 | 0 | } |
261 | | |
262 | 0 | fn to_i64(self) -> Option<i64> { |
263 | 0 | None |
264 | 0 | } |
265 | | } |
266 | | |
267 | | /// Allows conversion from supported Arrow types to a byte slice. |
268 | | pub trait ToByteSlice { |
269 | | /// Converts this instance into a byte slice |
270 | | fn to_byte_slice(&self) -> &[u8]; |
271 | | } |
272 | | |
273 | | impl<T: ArrowNativeType> ToByteSlice for [T] { |
274 | | #[inline] |
275 | | fn to_byte_slice(&self) -> &[u8] { |
276 | | let raw_ptr = self.as_ptr() as *const u8; |
277 | | unsafe { std::slice::from_raw_parts(raw_ptr, std::mem::size_of_val(self)) } |
278 | | } |
279 | | } |
280 | | |
281 | | impl<T: ArrowNativeType> ToByteSlice for T { |
282 | | #[inline] |
283 | 460 | fn to_byte_slice(&self) -> &[u8] { |
284 | 460 | let raw_ptr = self as *const T as *const u8; |
285 | 460 | unsafe { std::slice::from_raw_parts(raw_ptr, std::mem::size_of::<T>()) } |
286 | 460 | } |
287 | | } |
288 | | |
289 | | #[cfg(test)] |
290 | | mod tests { |
291 | | use super::*; |
292 | | |
293 | | #[test] |
294 | | fn test_i256() { |
295 | | let a = i256::from_parts(0, 0); |
296 | | assert_eq!(a.as_usize(), 0); |
297 | | assert_eq!(a.to_usize().unwrap(), 0); |
298 | | assert_eq!(a.to_isize().unwrap(), 0); |
299 | | |
300 | | let a = i256::from_parts(0, -1); |
301 | | assert_eq!(a.as_usize(), 0); |
302 | | assert!(a.to_usize().is_none()); |
303 | | assert!(a.to_usize().is_none()); |
304 | | |
305 | | let a = i256::from_parts(u128::MAX, -1); |
306 | | assert_eq!(a.as_usize(), usize::MAX); |
307 | | assert!(a.to_usize().is_none()); |
308 | | assert_eq!(a.to_isize().unwrap(), -1); |
309 | | } |
310 | | |
311 | | #[test] |
312 | | fn test_interval_usize() { |
313 | | assert_eq!(IntervalDayTime::new(1, 0).as_usize(), 1); |
314 | | assert_eq!(IntervalMonthDayNano::new(1, 0, 0).as_usize(), 1); |
315 | | |
316 | | let a = IntervalDayTime::new(23, 53); |
317 | | let b = IntervalDayTime::usize_as(a.as_usize()); |
318 | | assert_eq!(a, b); |
319 | | |
320 | | let a = IntervalMonthDayNano::new(23, 53, 0); |
321 | | let b = IntervalMonthDayNano::usize_as(a.as_usize()); |
322 | | assert_eq!(a, b); |
323 | | } |
324 | | } |