1 | // Copyright 2019 The Fuchsia Authors |
2 | // |
3 | // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 |
4 | // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT |
5 | // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. |
6 | // This file may not be copied, modified, or distributed except according to |
7 | // those terms. |
8 | |
9 | //! Byte order-aware numeric primitives. |
10 | //! |
11 | //! This module contains equivalents of the native multi-byte integer types with |
12 | //! no alignment requirement and supporting byte order conversions. |
13 | //! |
14 | //! For each native multi-byte integer type - `u16`, `i16`, `u32`, etc - and |
15 | //! floating point type - `f32` and `f64` - an equivalent type is defined by |
16 | //! this module - [`U16`], [`I16`], [`U32`], [`F64`], etc. Unlike their native |
17 | //! counterparts, these types have alignment 1, and take a type parameter |
18 | //! specifying the byte order in which the bytes are stored in memory. Each type |
19 | //! implements the [`FromBytes`], [`AsBytes`], and [`Unaligned`] traits. |
20 | //! |
21 | //! These two properties, taken together, make these types useful for defining |
22 | //! data structures whose memory layout matches a wire format such as that of a |
23 | //! network protocol or a file format. Such formats often have multi-byte values |
24 | //! at offsets that do not respect the alignment requirements of the equivalent |
25 | //! native types, and stored in a byte order not necessarily the same as that of |
26 | //! the target platform. |
27 | //! |
28 | //! Type aliases are provided for common byte orders in the [`big_endian`], |
29 | //! [`little_endian`], [`network_endian`], and [`native_endian`] submodules. |
30 | //! |
31 | //! # Example |
32 | //! |
33 | //! One use of these types is for representing network packet formats, such as |
34 | //! UDP: |
35 | //! |
36 | //! ```rust,edition2021 |
37 | //! # #[cfg (feature = "derive" )] { // This example uses derives, and won't compile without them |
38 | //! use zerocopy::{AsBytes, ByteSlice, FromBytes, FromZeroes, Ref, Unaligned}; |
39 | //! use zerocopy::byteorder::network_endian::U16; |
40 | //! |
41 | //! #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)] |
42 | //! #[repr(C)] |
43 | //! struct UdpHeader { |
44 | //! src_port: U16, |
45 | //! dst_port: U16, |
46 | //! length: U16, |
47 | //! checksum: U16, |
48 | //! } |
49 | //! |
50 | //! struct UdpPacket<B: ByteSlice> { |
51 | //! header: Ref<B, UdpHeader>, |
52 | //! body: B, |
53 | //! } |
54 | //! |
55 | //! impl<B: ByteSlice> UdpPacket<B> { |
56 | //! fn parse(bytes: B) -> Option<UdpPacket<B>> { |
57 | //! let (header, body) = Ref::new_from_prefix(bytes)?; |
58 | //! Some(UdpPacket { header, body }) |
59 | //! } |
60 | //! |
61 | //! fn src_port(&self) -> u16 { |
62 | //! self.header.src_port.get() |
63 | //! } |
64 | //! |
65 | //! // more getters... |
66 | //! } |
67 | //! # } |
68 | //! ``` |
69 | |
70 | use core::{ |
71 | convert::{TryFrom, TryInto}, |
72 | fmt::{self, Binary, Debug, Display, Formatter, LowerHex, Octal, UpperHex}, |
73 | marker::PhantomData, |
74 | num::TryFromIntError, |
75 | }; |
76 | |
77 | // We don't reexport `WriteBytesExt` or `ReadBytesExt` because those are only |
78 | // available with the `std` feature enabled, and zerocopy is `no_std` by |
79 | // default. |
80 | pub use ::byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, NetworkEndian, BE, LE}; |
81 | |
82 | use super::*; |
83 | |
84 | macro_rules! impl_fmt_trait { |
85 | ($name:ident, $native:ident, $trait:ident) => { |
86 | impl<O: ByteOrder> $trait for $name<O> { |
87 | #[inline(always)] |
88 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
89 | $trait::fmt(&self.get(), f) |
90 | } |
91 | } |
92 | }; |
93 | } |
94 | |
95 | macro_rules! impl_fmt_traits { |
96 | ($name:ident, $native:ident, "floating point number" ) => { |
97 | impl_fmt_trait!($name, $native, Display); |
98 | }; |
99 | ($name:ident, $native:ident, "unsigned integer" ) => { |
100 | impl_fmt_traits!($name, $native, @all_types); |
101 | }; |
102 | ($name:ident, $native:ident, "signed integer" ) => { |
103 | impl_fmt_traits!($name, $native, @all_types); |
104 | }; |
105 | ($name:ident, $native:ident, @all_types) => { |
106 | impl_fmt_trait!($name, $native, Display); |
107 | impl_fmt_trait!($name, $native, Octal); |
108 | impl_fmt_trait!($name, $native, LowerHex); |
109 | impl_fmt_trait!($name, $native, UpperHex); |
110 | impl_fmt_trait!($name, $native, Binary); |
111 | }; |
112 | } |
113 | |
114 | macro_rules! impl_ops_traits { |
115 | ($name:ident, $native:ident, "floating point number" ) => { |
116 | impl_ops_traits!($name, $native, @all_types); |
117 | impl_ops_traits!($name, $native, @signed_integer_floating_point); |
118 | }; |
119 | ($name:ident, $native:ident, "unsigned integer" ) => { |
120 | impl_ops_traits!($name, $native, @signed_unsigned_integer); |
121 | impl_ops_traits!($name, $native, @all_types); |
122 | }; |
123 | ($name:ident, $native:ident, "signed integer" ) => { |
124 | impl_ops_traits!($name, $native, @signed_unsigned_integer); |
125 | impl_ops_traits!($name, $native, @signed_integer_floating_point); |
126 | impl_ops_traits!($name, $native, @all_types); |
127 | }; |
128 | ($name:ident, $native:ident, @signed_unsigned_integer) => { |
129 | impl_ops_traits!(@without_byteorder_swap $name, $native, BitAnd, bitand, BitAndAssign, bitand_assign); |
130 | impl_ops_traits!(@without_byteorder_swap $name, $native, BitOr, bitor, BitOrAssign, bitor_assign); |
131 | impl_ops_traits!(@without_byteorder_swap $name, $native, BitXor, bitxor, BitXorAssign, bitxor_assign); |
132 | impl_ops_traits!(@with_byteorder_swap $name, $native, Shl, shl, ShlAssign, shl_assign); |
133 | impl_ops_traits!(@with_byteorder_swap $name, $native, Shr, shr, ShrAssign, shr_assign); |
134 | |
135 | impl<O> core::ops::Not for $name<O> { |
136 | type Output = $name<O>; |
137 | |
138 | #[inline(always)] |
139 | fn not(self) -> $name<O> { |
140 | let self_native = $native::from_ne_bytes(self.0); |
141 | $name((!self_native).to_ne_bytes(), PhantomData) |
142 | } |
143 | } |
144 | }; |
145 | ($name:ident, $native:ident, @signed_integer_floating_point) => { |
146 | impl<O: ByteOrder> core::ops::Neg for $name<O> { |
147 | type Output = $name<O>; |
148 | |
149 | #[inline(always)] |
150 | fn neg(self) -> $name<O> { |
151 | let self_native: $native = self.get(); |
152 | #[allow(clippy::arithmetic_side_effects)] |
153 | $name::<O>::new(-self_native) |
154 | } |
155 | } |
156 | }; |
157 | ($name:ident, $native:ident, @all_types) => { |
158 | impl_ops_traits!(@with_byteorder_swap $name, $native, Add, add, AddAssign, add_assign); |
159 | impl_ops_traits!(@with_byteorder_swap $name, $native, Div, div, DivAssign, div_assign); |
160 | impl_ops_traits!(@with_byteorder_swap $name, $native, Mul, mul, MulAssign, mul_assign); |
161 | impl_ops_traits!(@with_byteorder_swap $name, $native, Rem, rem, RemAssign, rem_assign); |
162 | impl_ops_traits!(@with_byteorder_swap $name, $native, Sub, sub, SubAssign, sub_assign); |
163 | }; |
164 | (@with_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => { |
165 | impl<O: ByteOrder> core::ops::$trait for $name<O> { |
166 | type Output = $name<O>; |
167 | |
168 | #[inline(always)] |
169 | fn $method(self, rhs: $name<O>) -> $name<O> { |
170 | let self_native: $native = self.get(); |
171 | let rhs_native: $native = rhs.get(); |
172 | let result_native = core::ops::$trait::$method(self_native, rhs_native); |
173 | $name::<O>::new(result_native) |
174 | } |
175 | } |
176 | |
177 | impl<O: ByteOrder> core::ops::$trait_assign for $name<O> { |
178 | #[inline(always)] |
179 | fn $method_assign(&mut self, rhs: $name<O>) { |
180 | *self = core::ops::$trait::$method(*self, rhs); |
181 | } |
182 | } |
183 | }; |
184 | // Implement traits in terms of the same trait on the native type, but |
185 | // without performing a byte order swap. This only works for bitwise |
186 | // operations like `&`, `|`, etc. |
187 | (@without_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => { |
188 | impl<O: ByteOrder> core::ops::$trait for $name<O> { |
189 | type Output = $name<O>; |
190 | |
191 | #[inline(always)] |
192 | fn $method(self, rhs: $name<O>) -> $name<O> { |
193 | let self_native = $native::from_ne_bytes(self.0); |
194 | let rhs_native = $native::from_ne_bytes(rhs.0); |
195 | let result_native = core::ops::$trait::$method(self_native, rhs_native); |
196 | $name(result_native.to_ne_bytes(), PhantomData) |
197 | } |
198 | } |
199 | |
200 | impl<O: ByteOrder> core::ops::$trait_assign for $name<O> { |
201 | #[inline(always)] |
202 | fn $method_assign(&mut self, rhs: $name<O>) { |
203 | *self = core::ops::$trait::$method(*self, rhs); |
204 | } |
205 | } |
206 | }; |
207 | } |
208 | |
209 | macro_rules! doc_comment { |
210 | ($x:expr, $($tt:tt)*) => { |
211 | #[doc = $x] |
212 | $($tt)* |
213 | }; |
214 | } |
215 | |
216 | macro_rules! define_max_value_constant { |
217 | ($name:ident, $bytes:expr, "unsigned integer" ) => { |
218 | /// The maximum value. |
219 | /// |
220 | /// This constant should be preferred to constructing a new value using |
221 | /// `new`, as `new` may perform an endianness swap depending on the |
222 | /// endianness `O` and the endianness of the platform. |
223 | pub const MAX_VALUE: $name<O> = $name([0xFFu8; $bytes], PhantomData); |
224 | }; |
225 | // We don't provide maximum and minimum value constants for signed values |
226 | // and floats because there's no way to do it generically - it would require |
227 | // a different value depending on the value of the `ByteOrder` type |
228 | // parameter. Currently, one workaround would be to provide implementations |
229 | // for concrete implementations of that trait. In the long term, if we are |
230 | // ever able to make the `new` constructor a const fn, we could use that |
231 | // instead. |
232 | ($name:ident, $bytes:expr, "signed integer" ) => {}; |
233 | ($name:ident, $bytes:expr, "floating point number" ) => {}; |
234 | } |
235 | |
236 | macro_rules! define_type { |
237 | ($article:ident, |
238 | $name:ident, |
239 | $native:ident, |
240 | $bits:expr, |
241 | $bytes:expr, |
242 | $read_method:ident, |
243 | $write_method:ident, |
244 | $number_kind:tt, |
245 | [$($larger_native:ty),*], |
246 | [$($larger_native_try:ty),*], |
247 | [$($larger_byteorder:ident),*], |
248 | [$($larger_byteorder_try:ident),*]) => { |
249 | doc_comment! { |
250 | concat!("A " , stringify!($bits), "-bit " , $number_kind, |
251 | " stored in a given byte order. |
252 | |
253 | `" , stringify!($name), "` is like the native `" , stringify!($native), "` type with |
254 | two major differences: First, it has no alignment requirement (its alignment is 1). |
255 | Second, the endianness of its memory layout is given by the type parameter `O`, |
256 | which can be any type which implements [`ByteOrder`]. In particular, this refers |
257 | to [`BigEndian`], [`LittleEndian`], [`NativeEndian`], and [`NetworkEndian`]. |
258 | |
259 | " , stringify!($article), " `" , stringify!($name), "` can be constructed using |
260 | the [`new`] method, and its contained value can be obtained as a native |
261 | `" ,stringify!($native), "` using the [`get`] method, or updated in place with |
262 | the [`set`] method. In all cases, if the endianness `O` is not the same as the |
263 | endianness of the current platform, an endianness swap will be performed in |
264 | order to uphold the invariants that a) the layout of `" , stringify!($name), "` |
265 | has endianness `O` and that, b) the layout of `" , stringify!($native), "` has |
266 | the platform's native endianness. |
267 | |
268 | `" , stringify!($name), "` implements [`FromBytes`], [`AsBytes`], and [`Unaligned`], |
269 | making it useful for parsing and serialization. See the module documentation for an |
270 | example of how it can be used for parsing UDP packets. |
271 | |
272 | [`new`]: crate::byteorder::" , stringify!($name), "::new |
273 | [`get`]: crate::byteorder::" , stringify!($name), "::get |
274 | [`set`]: crate::byteorder::" , stringify!($name), "::set |
275 | [`FromBytes`]: crate::FromBytes |
276 | [`AsBytes`]: crate::AsBytes |
277 | [`Unaligned`]: crate::Unaligned" ), |
278 | #[derive(Copy, Clone, Eq, PartialEq, Hash)] |
279 | #[cfg_attr(any(feature = "derive" , test), derive(KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned))] |
280 | #[repr(transparent)] |
281 | pub struct $name<O>([u8; $bytes], PhantomData<O>); |
282 | } |
283 | |
284 | #[cfg(not(any(feature = "derive" , test)))] |
285 | impl_known_layout!(O => $name<O>); |
286 | |
287 | safety_comment! { |
288 | /// SAFETY: |
289 | /// `$name<O>` is `repr(transparent)`, and so it has the same layout |
290 | /// as its only non-zero field, which is a `u8` array. `u8` arrays |
291 | /// are `FromZeroes`, `FromBytes`, `AsBytes`, and `Unaligned`. |
292 | impl_or_verify!(O => FromZeroes for $name<O>); |
293 | impl_or_verify!(O => FromBytes for $name<O>); |
294 | impl_or_verify!(O => AsBytes for $name<O>); |
295 | impl_or_verify!(O => Unaligned for $name<O>); |
296 | } |
297 | |
298 | impl<O> Default for $name<O> { |
299 | #[inline(always)] |
300 | fn default() -> $name<O> { |
301 | $name::ZERO |
302 | } |
303 | } |
304 | |
305 | impl<O> $name<O> { |
306 | /// The value zero. |
307 | /// |
308 | /// This constant should be preferred to constructing a new value |
309 | /// using `new`, as `new` may perform an endianness swap depending |
310 | /// on the endianness and platform. |
311 | pub const ZERO: $name<O> = $name([0u8; $bytes], PhantomData); |
312 | |
313 | define_max_value_constant!($name, $bytes, $number_kind); |
314 | |
315 | /// Constructs a new value from bytes which are already in the |
316 | /// endianness `O`. |
317 | #[inline(always)] |
318 | pub const fn from_bytes(bytes: [u8; $bytes]) -> $name<O> { |
319 | $name(bytes, PhantomData) |
320 | } |
321 | } |
322 | |
323 | impl<O: ByteOrder> $name<O> { |
324 | // TODO(joshlf): Make these const fns if the `ByteOrder` methods |
325 | // ever become const fns. |
326 | |
327 | /// Constructs a new value, possibly performing an endianness swap |
328 | /// to guarantee that the returned value has endianness `O`. |
329 | #[inline(always)] |
330 | pub fn new(n: $native) -> $name<O> { |
331 | let mut out = $name::default(); |
332 | O::$write_method(&mut out.0[..], n); |
333 | out |
334 | } |
335 | |
336 | /// Returns the value as a primitive type, possibly performing an |
337 | /// endianness swap to guarantee that the return value has the |
338 | /// endianness of the native platform. |
339 | #[inline(always)] |
340 | pub fn get(self) -> $native { |
341 | O::$read_method(&self.0[..]) |
342 | } |
343 | |
344 | /// Updates the value in place as a primitive type, possibly |
345 | /// performing an endianness swap to guarantee that the stored value |
346 | /// has the endianness `O`. |
347 | #[inline(always)] |
348 | pub fn set(&mut self, n: $native) { |
349 | O::$write_method(&mut self.0[..], n); |
350 | } |
351 | } |
352 | |
353 | // The reasoning behind which traits to implement here is to only |
354 | // implement traits which won't cause inference issues. Notably, |
355 | // comparison traits like PartialEq and PartialOrd tend to cause |
356 | // inference issues. |
357 | |
358 | impl<O: ByteOrder> From<$name<O>> for [u8; $bytes] { |
359 | #[inline(always)] |
360 | fn from(x: $name<O>) -> [u8; $bytes] { |
361 | x.0 |
362 | } |
363 | } |
364 | |
365 | impl<O: ByteOrder> From<[u8; $bytes]> for $name<O> { |
366 | #[inline(always)] |
367 | fn from(bytes: [u8; $bytes]) -> $name<O> { |
368 | $name(bytes, PhantomData) |
369 | } |
370 | } |
371 | |
372 | impl<O: ByteOrder> From<$name<O>> for $native { |
373 | #[inline(always)] |
374 | fn from(x: $name<O>) -> $native { |
375 | x.get() |
376 | } |
377 | } |
378 | |
379 | impl<O: ByteOrder> From<$native> for $name<O> { |
380 | #[inline(always)] |
381 | fn from(x: $native) -> $name<O> { |
382 | $name::new(x) |
383 | } |
384 | } |
385 | |
386 | $( |
387 | impl<O: ByteOrder> From<$name<O>> for $larger_native { |
388 | #[inline(always)] |
389 | fn from(x: $name<O>) -> $larger_native { |
390 | x.get().into() |
391 | } |
392 | } |
393 | )* |
394 | |
395 | $( |
396 | impl<O: ByteOrder> TryFrom<$larger_native_try> for $name<O> { |
397 | type Error = TryFromIntError; |
398 | #[inline(always)] |
399 | fn try_from(x: $larger_native_try) -> Result<$name<O>, TryFromIntError> { |
400 | $native::try_from(x).map($name::new) |
401 | } |
402 | } |
403 | )* |
404 | |
405 | $( |
406 | impl<O: ByteOrder, P: ByteOrder> From<$name<O>> for $larger_byteorder<P> { |
407 | #[inline(always)] |
408 | fn from(x: $name<O>) -> $larger_byteorder<P> { |
409 | $larger_byteorder::new(x.get().into()) |
410 | } |
411 | } |
412 | )* |
413 | |
414 | $( |
415 | impl<O: ByteOrder, P: ByteOrder> TryFrom<$larger_byteorder_try<P>> for $name<O> { |
416 | type Error = TryFromIntError; |
417 | #[inline(always)] |
418 | fn try_from(x: $larger_byteorder_try<P>) -> Result<$name<O>, TryFromIntError> { |
419 | x.get().try_into().map($name::new) |
420 | } |
421 | } |
422 | )* |
423 | |
424 | impl<O: ByteOrder> AsRef<[u8; $bytes]> for $name<O> { |
425 | #[inline(always)] |
426 | fn as_ref(&self) -> &[u8; $bytes] { |
427 | &self.0 |
428 | } |
429 | } |
430 | |
431 | impl<O: ByteOrder> AsMut<[u8; $bytes]> for $name<O> { |
432 | #[inline(always)] |
433 | fn as_mut(&mut self) -> &mut [u8; $bytes] { |
434 | &mut self.0 |
435 | } |
436 | } |
437 | |
438 | impl<O: ByteOrder> PartialEq<$name<O>> for [u8; $bytes] { |
439 | #[inline(always)] |
440 | fn eq(&self, other: &$name<O>) -> bool { |
441 | self.eq(&other.0) |
442 | } |
443 | } |
444 | |
445 | impl<O: ByteOrder> PartialEq<[u8; $bytes]> for $name<O> { |
446 | #[inline(always)] |
447 | fn eq(&self, other: &[u8; $bytes]) -> bool { |
448 | self.0.eq(other) |
449 | } |
450 | } |
451 | |
452 | impl_fmt_traits!($name, $native, $number_kind); |
453 | impl_ops_traits!($name, $native, $number_kind); |
454 | |
455 | impl<O: ByteOrder> Debug for $name<O> { |
456 | #[inline] |
457 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
458 | // This results in a format like "U16(42)". |
459 | f.debug_tuple(stringify!($name)).field(&self.get()).finish() |
460 | } |
461 | } |
462 | }; |
463 | } |
464 | |
465 | define_type!( |
466 | A, |
467 | U16, |
468 | u16, |
469 | 16, |
470 | 2, |
471 | read_u16, |
472 | write_u16, |
473 | "unsigned integer" , |
474 | [u32, u64, u128, usize], |
475 | [u32, u64, u128, usize], |
476 | [U32, U64, U128], |
477 | [U32, U64, U128] |
478 | ); |
479 | define_type!( |
480 | A, |
481 | U32, |
482 | u32, |
483 | 32, |
484 | 4, |
485 | read_u32, |
486 | write_u32, |
487 | "unsigned integer" , |
488 | [u64, u128], |
489 | [u64, u128], |
490 | [U64, U128], |
491 | [U64, U128] |
492 | ); |
493 | define_type!( |
494 | A, |
495 | U64, |
496 | u64, |
497 | 64, |
498 | 8, |
499 | read_u64, |
500 | write_u64, |
501 | "unsigned integer" , |
502 | [u128], |
503 | [u128], |
504 | [U128], |
505 | [U128] |
506 | ); |
507 | define_type!(A, U128, u128, 128, 16, read_u128, write_u128, "unsigned integer" , [], [], [], []); |
508 | define_type!( |
509 | An, |
510 | I16, |
511 | i16, |
512 | 16, |
513 | 2, |
514 | read_i16, |
515 | write_i16, |
516 | "signed integer" , |
517 | [i32, i64, i128, isize], |
518 | [i32, i64, i128, isize], |
519 | [I32, I64, I128], |
520 | [I32, I64, I128] |
521 | ); |
522 | define_type!( |
523 | An, |
524 | I32, |
525 | i32, |
526 | 32, |
527 | 4, |
528 | read_i32, |
529 | write_i32, |
530 | "signed integer" , |
531 | [i64, i128], |
532 | [i64, i128], |
533 | [I64, I128], |
534 | [I64, I128] |
535 | ); |
536 | define_type!( |
537 | An, |
538 | I64, |
539 | i64, |
540 | 64, |
541 | 8, |
542 | read_i64, |
543 | write_i64, |
544 | "signed integer" , |
545 | [i128], |
546 | [i128], |
547 | [I128], |
548 | [I128] |
549 | ); |
550 | define_type!(An, I128, i128, 128, 16, read_i128, write_i128, "signed integer" , [], [], [], []); |
551 | define_type!( |
552 | An, |
553 | F32, |
554 | f32, |
555 | 32, |
556 | 4, |
557 | read_f32, |
558 | write_f32, |
559 | "floating point number" , |
560 | [f64], |
561 | [], |
562 | [F64], |
563 | [] |
564 | ); |
565 | define_type!(An, F64, f64, 64, 8, read_f64, write_f64, "floating point number" , [], [], [], []); |
566 | |
567 | macro_rules! module { |
568 | ($name:ident, $trait:ident, $endianness_str:expr) => { |
569 | /// Numeric primitives stored in |
570 | #[doc = $endianness_str] |
571 | /// byte order. |
572 | pub mod $name { |
573 | use byteorder::$trait; |
574 | |
575 | module!(@ty U16, $trait, "16-bit unsigned integer" , $endianness_str); |
576 | module!(@ty U32, $trait, "32-bit unsigned integer" , $endianness_str); |
577 | module!(@ty U64, $trait, "64-bit unsigned integer" , $endianness_str); |
578 | module!(@ty U128, $trait, "128-bit unsigned integer" , $endianness_str); |
579 | module!(@ty I16, $trait, "16-bit signed integer" , $endianness_str); |
580 | module!(@ty I32, $trait, "32-bit signed integer" , $endianness_str); |
581 | module!(@ty I64, $trait, "64-bit signed integer" , $endianness_str); |
582 | module!(@ty I128, $trait, "128-bit signed integer" , $endianness_str); |
583 | module!(@ty F32, $trait, "32-bit floating point number" , $endianness_str); |
584 | module!(@ty F64, $trait, "64-bit floating point number" , $endianness_str); |
585 | } |
586 | }; |
587 | (@ty $ty:ident, $trait:ident, $desc_str:expr, $endianness_str:expr) => { |
588 | /// A |
589 | #[doc = $desc_str] |
590 | /// stored in |
591 | #[doc = $endianness_str] |
592 | /// byte order. |
593 | pub type $ty = crate::byteorder::$ty<$trait>; |
594 | }; |
595 | } |
596 | |
597 | module!(big_endian, BigEndian, "big-endian" ); |
598 | module!(little_endian, LittleEndian, "little-endian" ); |
599 | module!(network_endian, NetworkEndian, "network-endian" ); |
600 | module!(native_endian, NativeEndian, "native-endian" ); |
601 | |
602 | #[cfg (any(test, kani))] |
603 | mod tests { |
604 | use ::byteorder::NativeEndian; |
605 | |
606 | use { |
607 | super::*, |
608 | crate::{AsBytes, FromBytes, Unaligned}, |
609 | }; |
610 | |
611 | #[cfg (not(kani))] |
612 | mod compatibility { |
613 | pub(super) use rand::{ |
614 | distributions::{Distribution, Standard}, |
615 | rngs::SmallRng, |
616 | Rng, SeedableRng, |
617 | }; |
618 | |
619 | pub(crate) trait Arbitrary {} |
620 | |
621 | impl<T> Arbitrary for T {} |
622 | } |
623 | |
624 | #[cfg (kani)] |
625 | mod compatibility { |
626 | pub(crate) use kani::Arbitrary; |
627 | |
628 | pub(crate) struct SmallRng; |
629 | |
630 | impl SmallRng { |
631 | pub(crate) fn seed_from_u64(_state: u64) -> Self { |
632 | Self |
633 | } |
634 | } |
635 | |
636 | pub(crate) trait Rng { |
637 | fn sample<T, D: Distribution<T>>(&mut self, _distr: D) -> T |
638 | where |
639 | T: Arbitrary, |
640 | { |
641 | kani::any() |
642 | } |
643 | } |
644 | |
645 | impl Rng for SmallRng {} |
646 | |
647 | pub(crate) trait Distribution<T> {} |
648 | impl<T, U> Distribution<T> for U {} |
649 | |
650 | pub(crate) struct Standard; |
651 | } |
652 | |
653 | use compatibility::*; |
654 | |
655 | // A native integer type (u16, i32, etc). |
656 | #[cfg_attr (kani, allow(dead_code))] |
657 | trait Native: Arbitrary + FromBytes + AsBytes + Copy + PartialEq + Debug { |
658 | const ZERO: Self; |
659 | const MAX_VALUE: Self; |
660 | |
661 | type Distribution: Distribution<Self>; |
662 | const DIST: Self::Distribution; |
663 | |
664 | fn rand<R: Rng>(rng: &mut R) -> Self { |
665 | rng.sample(Self::DIST) |
666 | } |
667 | |
668 | fn checked_add(self, rhs: Self) -> Option<Self>; |
669 | fn checked_div(self, rhs: Self) -> Option<Self>; |
670 | fn checked_mul(self, rhs: Self) -> Option<Self>; |
671 | fn checked_rem(self, rhs: Self) -> Option<Self>; |
672 | fn checked_sub(self, rhs: Self) -> Option<Self>; |
673 | fn checked_shl(self, rhs: Self) -> Option<Self>; |
674 | fn checked_shr(self, rhs: Self) -> Option<Self>; |
675 | |
676 | fn is_nan(self) -> bool; |
677 | |
678 | /// For `f32` and `f64`, NaN values are not considered equal to |
679 | /// themselves. This method is like `assert_eq!`, but it treats NaN |
680 | /// values as equal. |
681 | fn assert_eq_or_nan(self, other: Self) { |
682 | let slf = (!self.is_nan()).then(|| self); |
683 | let other = (!other.is_nan()).then(|| other); |
684 | assert_eq!(slf, other); |
685 | } |
686 | } |
687 | |
688 | trait ByteArray: |
689 | FromBytes + AsBytes + Copy + AsRef<[u8]> + AsMut<[u8]> + Debug + Default + Eq |
690 | { |
691 | /// Invert the order of the bytes in the array. |
692 | fn invert(self) -> Self; |
693 | } |
694 | |
695 | trait ByteOrderType: FromBytes + AsBytes + Unaligned + Copy + Eq + Debug { |
696 | type Native: Native; |
697 | type ByteArray: ByteArray; |
698 | |
699 | const ZERO: Self; |
700 | |
701 | fn new(native: Self::Native) -> Self; |
702 | fn get(self) -> Self::Native; |
703 | fn set(&mut self, native: Self::Native); |
704 | fn from_bytes(bytes: Self::ByteArray) -> Self; |
705 | fn into_bytes(self) -> Self::ByteArray; |
706 | |
707 | /// For `f32` and `f64`, NaN values are not considered equal to |
708 | /// themselves. This method is like `assert_eq!`, but it treats NaN |
709 | /// values as equal. |
710 | fn assert_eq_or_nan(self, other: Self) { |
711 | let slf = (!self.get().is_nan()).then(|| self); |
712 | let other = (!other.get().is_nan()).then(|| other); |
713 | assert_eq!(slf, other); |
714 | } |
715 | } |
716 | |
717 | trait ByteOrderTypeUnsigned: ByteOrderType { |
718 | const MAX_VALUE: Self; |
719 | } |
720 | |
721 | macro_rules! impl_byte_array { |
722 | ($bytes:expr) => { |
723 | impl ByteArray for [u8; $bytes] { |
724 | fn invert(mut self) -> [u8; $bytes] { |
725 | self.reverse(); |
726 | self |
727 | } |
728 | } |
729 | }; |
730 | } |
731 | |
732 | impl_byte_array!(2); |
733 | impl_byte_array!(4); |
734 | impl_byte_array!(8); |
735 | impl_byte_array!(16); |
736 | |
737 | macro_rules! impl_byte_order_type_unsigned { |
738 | ($name:ident, unsigned) => { |
739 | impl<O: ByteOrder> ByteOrderTypeUnsigned for $name<O> { |
740 | const MAX_VALUE: $name<O> = $name::MAX_VALUE; |
741 | } |
742 | }; |
743 | ($name:ident, signed) => {}; |
744 | } |
745 | |
746 | macro_rules! impl_traits { |
747 | ($name:ident, $native:ident, $bytes:expr, $sign:ident $(, @$float:ident)?) => { |
748 | impl Native for $native { |
749 | // For some types, `0 as $native` is required (for example, when |
750 | // `$native` is a floating-point type; `0` is an integer), but |
751 | // for other types, it's a trivial cast. In all cases, Clippy |
752 | // thinks it's dangerous. |
753 | #[allow(trivial_numeric_casts, clippy::as_conversions)] |
754 | const ZERO: $native = 0 as $native; |
755 | const MAX_VALUE: $native = $native::MAX; |
756 | |
757 | type Distribution = Standard; |
758 | const DIST: Standard = Standard; |
759 | |
760 | impl_traits!(@float_dependent_methods $(@$float)?); |
761 | } |
762 | |
763 | impl<O: ByteOrder> ByteOrderType for $name<O> { |
764 | type Native = $native; |
765 | type ByteArray = [u8; $bytes]; |
766 | |
767 | const ZERO: $name<O> = $name::ZERO; |
768 | |
769 | fn new(native: $native) -> $name<O> { |
770 | $name::new(native) |
771 | } |
772 | |
773 | fn get(self) -> $native { |
774 | $name::get(self) |
775 | } |
776 | |
777 | fn set(&mut self, native: $native) { |
778 | $name::set(self, native) |
779 | } |
780 | |
781 | fn from_bytes(bytes: [u8; $bytes]) -> $name<O> { |
782 | $name::from(bytes) |
783 | } |
784 | |
785 | fn into_bytes(self) -> [u8; $bytes] { |
786 | <[u8; $bytes]>::from(self) |
787 | } |
788 | } |
789 | |
790 | impl_byte_order_type_unsigned!($name, $sign); |
791 | }; |
792 | (@float_dependent_methods) => { |
793 | fn checked_add(self, rhs: Self) -> Option<Self> { self.checked_add(rhs) } |
794 | fn checked_div(self, rhs: Self) -> Option<Self> { self.checked_div(rhs) } |
795 | fn checked_mul(self, rhs: Self) -> Option<Self> { self.checked_mul(rhs) } |
796 | fn checked_rem(self, rhs: Self) -> Option<Self> { self.checked_rem(rhs) } |
797 | fn checked_sub(self, rhs: Self) -> Option<Self> { self.checked_sub(rhs) } |
798 | fn checked_shl(self, rhs: Self) -> Option<Self> { self.checked_shl(rhs.try_into().unwrap_or(u32::MAX)) } |
799 | fn checked_shr(self, rhs: Self) -> Option<Self> { self.checked_shr(rhs.try_into().unwrap_or(u32::MAX)) } |
800 | fn is_nan(self) -> bool { false } |
801 | }; |
802 | (@float_dependent_methods @float) => { |
803 | fn checked_add(self, rhs: Self) -> Option<Self> { Some(self + rhs) } |
804 | fn checked_div(self, rhs: Self) -> Option<Self> { Some(self / rhs) } |
805 | fn checked_mul(self, rhs: Self) -> Option<Self> { Some(self * rhs) } |
806 | fn checked_rem(self, rhs: Self) -> Option<Self> { Some(self % rhs) } |
807 | fn checked_sub(self, rhs: Self) -> Option<Self> { Some(self - rhs) } |
808 | fn checked_shl(self, _rhs: Self) -> Option<Self> { unimplemented!() } |
809 | fn checked_shr(self, _rhs: Self) -> Option<Self> { unimplemented!() } |
810 | fn is_nan(self) -> bool { self.is_nan() } |
811 | }; |
812 | } |
813 | |
814 | impl_traits!(U16, u16, 2, unsigned); |
815 | impl_traits!(U32, u32, 4, unsigned); |
816 | impl_traits!(U64, u64, 8, unsigned); |
817 | impl_traits!(U128, u128, 16, unsigned); |
818 | impl_traits!(I16, i16, 2, signed); |
819 | impl_traits!(I32, i32, 4, signed); |
820 | impl_traits!(I64, i64, 8, signed); |
821 | impl_traits!(I128, i128, 16, signed); |
822 | impl_traits!(F32, f32, 4, signed, @float); |
823 | impl_traits!(F64, f64, 8, signed, @float); |
824 | |
825 | macro_rules! call_for_unsigned_types { |
826 | ($fn:ident, $byteorder:ident) => { |
827 | $fn::<U16<$byteorder>>(); |
828 | $fn::<U32<$byteorder>>(); |
829 | $fn::<U64<$byteorder>>(); |
830 | $fn::<U128<$byteorder>>(); |
831 | }; |
832 | } |
833 | |
834 | macro_rules! call_for_signed_types { |
835 | ($fn:ident, $byteorder:ident) => { |
836 | $fn::<I16<$byteorder>>(); |
837 | $fn::<I32<$byteorder>>(); |
838 | $fn::<I64<$byteorder>>(); |
839 | $fn::<I128<$byteorder>>(); |
840 | }; |
841 | } |
842 | |
843 | macro_rules! call_for_float_types { |
844 | ($fn:ident, $byteorder:ident) => { |
845 | $fn::<F32<$byteorder>>(); |
846 | $fn::<F64<$byteorder>>(); |
847 | }; |
848 | } |
849 | |
850 | macro_rules! call_for_all_types { |
851 | ($fn:ident, $byteorder:ident) => { |
852 | call_for_unsigned_types!($fn, $byteorder); |
853 | call_for_signed_types!($fn, $byteorder); |
854 | call_for_float_types!($fn, $byteorder); |
855 | }; |
856 | } |
857 | |
858 | #[cfg (target_endian = "big" )] |
859 | type NonNativeEndian = LittleEndian; |
860 | #[cfg (target_endian = "little" )] |
861 | type NonNativeEndian = BigEndian; |
862 | |
863 | // We use a `u64` seed so that we can use `SeedableRng::seed_from_u64`. |
864 | // `SmallRng`'s `SeedableRng::Seed` differs by platform, so if we wanted to |
865 | // call `SeedableRng::from_seed`, which takes a `Seed`, we would need |
866 | // conditional compilation by `target_pointer_width`. |
867 | const RNG_SEED: u64 = 0x7A03CAE2F32B5B8F; |
868 | |
869 | const RAND_ITERS: usize = if cfg!(any(miri, kani)) { |
870 | // The tests below which use this constant used to take a very long time |
871 | // on Miri, which slows down local development and CI jobs. We're not |
872 | // using Miri to check for the correctness of our code, but rather its |
873 | // soundness, and at least in the context of these particular tests, a |
874 | // single loop iteration is just as good for surfacing UB as multiple |
875 | // iterations are. |
876 | // |
877 | // As of the writing of this comment, here's one set of measurements: |
878 | // |
879 | // $ # RAND_ITERS == 1 |
880 | // $ cargo miri test -- -Z unstable-options --report-time endian |
881 | // test byteorder::tests::test_native_endian ... ok <0.049s> |
882 | // test byteorder::tests::test_non_native_endian ... ok <0.061s> |
883 | // |
884 | // $ # RAND_ITERS == 1024 |
885 | // $ cargo miri test -- -Z unstable-options --report-time endian |
886 | // test byteorder::tests::test_native_endian ... ok <25.716s> |
887 | // test byteorder::tests::test_non_native_endian ... ok <38.127s> |
888 | 1 |
889 | } else { |
890 | 1024 |
891 | }; |
892 | |
893 | #[cfg_attr (test, test)] |
894 | #[cfg_attr (kani, kani::proof)] |
895 | fn test_zero() { |
896 | fn test_zero<T: ByteOrderType>() { |
897 | assert_eq!(T::ZERO.get(), T::Native::ZERO); |
898 | } |
899 | |
900 | call_for_all_types!(test_zero, NativeEndian); |
901 | call_for_all_types!(test_zero, NonNativeEndian); |
902 | } |
903 | |
904 | #[cfg_attr (test, test)] |
905 | #[cfg_attr (kani, kani::proof)] |
906 | fn test_max_value() { |
907 | fn test_max_value<T: ByteOrderTypeUnsigned>() { |
908 | assert_eq!(T::MAX_VALUE.get(), T::Native::MAX_VALUE); |
909 | } |
910 | |
911 | call_for_unsigned_types!(test_max_value, NativeEndian); |
912 | call_for_unsigned_types!(test_max_value, NonNativeEndian); |
913 | } |
914 | |
915 | #[cfg_attr (test, test)] |
916 | #[cfg_attr (kani, kani::proof)] |
917 | fn test_endian() { |
918 | fn test<T: ByteOrderType>(invert: bool) { |
919 | let mut r = SmallRng::seed_from_u64(RNG_SEED); |
920 | for _ in 0..RAND_ITERS { |
921 | let native = T::Native::rand(&mut r); |
922 | let mut bytes = T::ByteArray::default(); |
923 | bytes.as_bytes_mut().copy_from_slice(native.as_bytes()); |
924 | if invert { |
925 | bytes = bytes.invert(); |
926 | } |
927 | let mut from_native = T::new(native); |
928 | let from_bytes = T::from_bytes(bytes); |
929 | |
930 | from_native.assert_eq_or_nan(from_bytes); |
931 | from_native.get().assert_eq_or_nan(native); |
932 | from_bytes.get().assert_eq_or_nan(native); |
933 | |
934 | assert_eq!(from_native.into_bytes(), bytes); |
935 | assert_eq!(from_bytes.into_bytes(), bytes); |
936 | |
937 | let updated = T::Native::rand(&mut r); |
938 | from_native.set(updated); |
939 | from_native.get().assert_eq_or_nan(updated); |
940 | } |
941 | } |
942 | |
943 | fn test_native<T: ByteOrderType>() { |
944 | test ::<T>(false); |
945 | } |
946 | |
947 | fn test_non_native<T: ByteOrderType>() { |
948 | test ::<T>(true); |
949 | } |
950 | |
951 | call_for_all_types!(test_native, NativeEndian); |
952 | call_for_all_types!(test_non_native, NonNativeEndian); |
953 | } |
954 | |
955 | #[test ] |
956 | fn test_ops_impls() { |
957 | // Test implementations of traits in `core::ops`. Some of these are |
958 | // fairly banal, but some are optimized to perform the operation without |
959 | // swapping byte order (namely, bit-wise operations which are identical |
960 | // regardless of byte order). These are important to test, and while |
961 | // we're testing those anyway, it's trivial to test all of the impls. |
962 | |
963 | fn test<T, F, G, H>(op: F, op_native: G, op_native_checked: Option<H>) |
964 | where |
965 | T: ByteOrderType, |
966 | F: Fn(T, T) -> T, |
967 | G: Fn(T::Native, T::Native) -> T::Native, |
968 | H: Fn(T::Native, T::Native) -> Option<T::Native>, |
969 | { |
970 | let mut r = SmallRng::seed_from_u64(RNG_SEED); |
971 | for _ in 0..RAND_ITERS { |
972 | let n0 = T::Native::rand(&mut r); |
973 | let n1 = T::Native::rand(&mut r); |
974 | let t0 = T::new(n0); |
975 | let t1 = T::new(n1); |
976 | |
977 | // If this operation would overflow/underflow, skip it rather |
978 | // than attempt to catch and recover from panics. |
979 | if matches!(&op_native_checked, Some(checked) if checked(n0, n1).is_none()) { |
980 | continue; |
981 | } |
982 | |
983 | let n_res = op_native(n0, n1); |
984 | let t_res = op(t0, t1); |
985 | |
986 | // For `f32` and `f64`, NaN values are not considered equal to |
987 | // themselves. We store `Option<f32>`/`Option<f64>` and store |
988 | // NaN as `None` so they can still be compared. |
989 | let n_res = (!T::Native::is_nan(n_res)).then(|| n_res); |
990 | let t_res = (!T::Native::is_nan(t_res.get())).then(|| t_res.get()); |
991 | assert_eq!(n_res, t_res); |
992 | } |
993 | } |
994 | |
995 | macro_rules! test { |
996 | (@binary $trait:ident, $method:ident $([$checked_method:ident])?, $($call_for_macros:ident),*) => {{ |
997 | test!( |
998 | @inner $trait, |
999 | core::ops::$trait::$method, |
1000 | core::ops::$trait::$method, |
1001 | { |
1002 | #[allow(unused_mut, unused_assignments)] |
1003 | let mut op_native_checked = None::<fn(T::Native, T::Native) -> Option<T::Native>>; |
1004 | $( |
1005 | op_native_checked = Some(T::Native::$checked_method); |
1006 | )? |
1007 | op_native_checked |
1008 | }, |
1009 | $($call_for_macros),* |
1010 | ); |
1011 | }}; |
1012 | (@unary $trait:ident, $method:ident $([$checked_method:ident])?, $($call_for_macros:ident),*) => {{ |
1013 | test!( |
1014 | @inner $trait, |
1015 | |slf, _rhs| core::ops::$trait::$method(slf), |
1016 | |slf, _rhs| core::ops::$trait::$method(slf), |
1017 | { |
1018 | #[allow(unused_mut, unused_assignments)] |
1019 | let mut op_native_checked = None::<fn(T::Native, T::Native) -> Option<T::Native>>; |
1020 | $( |
1021 | op_native_checked = Some(|slf, _rhs| T::Native::$checked_method(slf)); |
1022 | )? |
1023 | op_native_checked |
1024 | }, |
1025 | $($call_for_macros),* |
1026 | ); |
1027 | }}; |
1028 | (@inner $trait:ident, $op:expr, $op_native:expr, $op_native_checked:expr, $($call_for_macros:ident),*) => {{ |
1029 | fn t<T: ByteOrderType + core::ops::$trait<Output = T>>() |
1030 | where |
1031 | T::Native: core::ops::$trait<Output = T::Native>, |
1032 | { |
1033 | test::<T, _, _, _>( |
1034 | $op, |
1035 | $op_native, |
1036 | $op_native_checked, |
1037 | ); |
1038 | } |
1039 | |
1040 | $( |
1041 | $call_for_macros!(t, NativeEndian); |
1042 | $call_for_macros!(t, NonNativeEndian); |
1043 | )* |
1044 | }}; |
1045 | } |
1046 | |
1047 | test !(@binary Add, add[checked_add], call_for_all_types); |
1048 | test !(@binary Div, div[checked_div], call_for_all_types); |
1049 | test !(@binary Mul, mul[checked_mul], call_for_all_types); |
1050 | test !(@binary Rem, rem[checked_rem], call_for_all_types); |
1051 | test !(@binary Sub, sub[checked_sub], call_for_all_types); |
1052 | |
1053 | test !(@binary BitAnd, bitand, call_for_unsigned_types, call_for_signed_types); |
1054 | test !(@binary BitOr, bitor, call_for_unsigned_types, call_for_signed_types); |
1055 | test !(@binary BitXor, bitxor, call_for_unsigned_types, call_for_signed_types); |
1056 | test !(@binary Shl, shl[checked_shl], call_for_unsigned_types, call_for_signed_types); |
1057 | test !(@binary Shr, shr[checked_shr], call_for_unsigned_types, call_for_signed_types); |
1058 | |
1059 | test !(@unary Not, not, call_for_signed_types, call_for_unsigned_types); |
1060 | test !(@unary Neg, neg, call_for_signed_types, call_for_float_types); |
1061 | } |
1062 | |
1063 | #[test ] |
1064 | fn test_debug_impl() { |
1065 | // Ensure that Debug applies format options to the inner value. |
1066 | let val = U16::<LE>::new(10); |
1067 | assert_eq!(format!("{:?}" , val), "U16(10)" ); |
1068 | assert_eq!(format!("{:03?}" , val), "U16(010)" ); |
1069 | assert_eq!(format!("{:x?}" , val), "U16(a)" ); |
1070 | } |
1071 | } |
1072 | |