musli_zerocopy/endian/
mod.rs

1//! Marker types which define a [`ByteOrder`] to use.
2
3/// A macro that picks which `$expr` to evaluate to based on if the current
4/// `#[cfg(target_endian = "..")]` matches `$endian` and optionally
5/// #[cfg(target_pointer_width = "..")] matches `$pointer_width`.
6///
7/// A fallback branch is supported with `_ => $expr`.
8///
9/// # Examples
10///
11/// ```no_run
12/// use musli_zerocopy::{endian, ByteOrder, Endian, Ref, ZeroCopy};
13///
14/// #[derive(ZeroCopy)]
15/// #[repr(C)]
16/// struct Header {
17///     big: Ref<Data<endian::Big>, endian::Big>,
18///     little: Ref<Data<endian::Little>, endian::Little>,
19/// }
20///
21/// #[derive(ZeroCopy)]
22/// #[repr(C)]
23/// struct Data<E = endian::Native>
24/// where
25///     E: ByteOrder,
26/// {
27///     name: Ref<str, E>,
28///     age: Endian<u32, E>,
29/// }
30///
31/// let header: Header = todo!();
32/// let data: Ref<Data> = endian::pick!("big" => header.big, "little" => header.little);
33/// // Example using fallback:
34/// let data: Ref<Data> = endian::pick!("big" => header.big, _ => header.little);
35/// ```
36///
37/// Note that this evaluates to a private type in case the current endianness is
38/// not covered:
39///
40/// ```compile_fail
41/// #[cfg(target_endian = "little")]
42/// let data: u32 = endian::pick!("big" => 1u32);
43/// #[cfg(target_endian = "big")]
44/// let data: u32 = endian::pick!("little" => 1u32);
45/// ```
46#[macro_export]
47#[doc(hidden)]
48macro_rules! __pick {
49    ($($endian:literal $(/ $pointer_width:literal)? => $expr:expr),+ $(, _ => $fallback:expr)? $(,)?) => {
50        match () {
51            $(
52                #[cfg(all(target_endian = $endian $(, target_pointer_width = $pointer_width)*))]
53                () => $expr,
54            )*
55            #[cfg(not(any($(all(target_endian = $endian $(, target_pointer_width = $pointer_width)*)),*)))]
56            () => $crate::__pick_fallback!($($fallback)*)
57        }
58    };
59}
60
61#[macro_export]
62#[doc(hidden)]
63macro_rules! __pick_fallback {
64    () => {
65        struct UnsupportedEndian;
66        UnsupportedEndian
67    };
68
69    ($expr:expr) => {
70        $expr
71    };
72}
73
74/// A macro that matches `$expr` to its associated `$pat` if the current
75/// `#[cfg(target_endian = "..")]` matches `$endian` and optionally
76/// #[cfg(target_pointer_width = "..")] matches `$pointer_width`.
77///
78/// Note that if running on a platform which is not covered, the result will
79/// always be `false`:
80///
81/// ```
82/// use musli_zerocopy::endian;
83///
84/// #[derive(Debug, PartialEq)]
85/// enum Endian { Little, Big }
86///
87/// let e = endian::pick!("little" => Endian::Little, "big" => Endian::Big);
88///
89/// #[cfg(target_endian = "little")]
90/// assert!(!endian::matches!(e, "big" => Big));
91/// #[cfg(target_endian = "big")]
92/// assert!(!endian::matches!(e, "little" => Little));
93/// ```
94///
95/// # Examples
96///
97/// ```
98/// use musli_zerocopy::endian;
99///
100/// #[derive(Debug, PartialEq)]
101/// enum Endian { Little, Big }
102///
103/// let e = endian::pick!("little" => Endian::Little, "big" => Endian::Big);
104///
105/// assert!(endian::matches!(e, "little" => Endian::Little, "big" => Endian::Big));
106/// ```
107#[macro_export]
108#[doc(hidden)]
109macro_rules! __matches {
110    ($expr:expr, $($endian:literal $(/ $pointer_width:literal)? => $pat:pat),+ $(,)?) => {
111        match $expr {
112            $(
113                #[cfg(all(target_endian = $endian $(, target_pointer_width = $pointer_width)*))]
114                value => matches!(value, $pat),
115            )*
116            #[cfg(not(any($(all(target_endian = $endian $(, target_pointer_width = $pointer_width)*)),*)))]
117            _ => false,
118        }
119    };
120}
121
122#[doc(inline)]
123pub use __pick as pick;
124
125#[doc(inline)]
126pub use __matches as matches;
127
128#[doc(inline)]
129pub use self::endian::Endian;
130mod endian;
131
132/// Alias for the native endian [`ByteOrder`].
133#[cfg(target_endian = "little")]
134pub type Native = Little;
135
136/// Alias for the native endian [`ByteOrder`].
137#[cfg(target_endian = "big")]
138pub type Native = Big;
139
140/// Alias for the opposite endian [`ByteOrder`].
141#[cfg(target_endian = "little")]
142pub type Other = Big;
143
144/// Alias for the opposite endian [`ByteOrder`].
145#[cfg(target_endian = "big")]
146pub type Other = Little;
147
148/// Marker type indicating that the big endian [`ByteOrder`] is in use.
149#[non_exhaustive]
150pub struct Big;
151
152/// Marker type indicating that the little endian [`ByteOrder`] is in use.
153#[non_exhaustive]
154pub struct Little;
155
156use crate::ZeroCopy;
157
158/// Convert the value `T` from [`Big`] to [`Native`] endian.
159///
160/// This ignores types which has [`ZeroCopy::CAN_SWAP_BYTES`] set to `false`,
161/// such as [`char`]. Such values will simply pass through.
162///
163/// Swapping the bytes of a type which explicitly records its own byte order
164/// like [`Ref<T>`] is a no-op.
165///
166/// [`Ref<T>`]: crate::Ref
167///
168/// # Examples
169///
170/// ```
171/// use musli_zerocopy::{endian, ZeroCopy};
172///
173/// #[derive(Debug, PartialEq, ZeroCopy)]
174/// #[repr(C)]
175/// struct Struct {
176///     c: char,
177///     bits32: u32,
178///     bits64: u64,
179/// }
180///
181/// let st = endian::from_be(Struct {
182///     c: 'a',
183///     bits32: 0x10203040u32.to_be(),
184///     bits64: 0x5060708090a0b0c0u64.to_be(),
185/// });
186///
187/// assert_eq!(st, Struct {
188///     c: 'a',
189///     bits32: 0x10203040,
190///     bits64: 0x5060708090a0b0c0,
191/// });
192/// ```
193pub fn from_be<T>(value: T) -> T
194where
195    T: ZeroCopy,
196{
197    from_endian::<_, Big>(value)
198}
199
200/// Convert the value `T` from [`Little`] to [`Native`] endian.
201///
202/// This ignores types which has [`ZeroCopy::CAN_SWAP_BYTES`] set to `false`,
203/// such as [`char`]. Such values will simply pass through.
204///
205/// Swapping the bytes of a type which explicitly records its own byte order
206/// like [`Ref<T>`] is a no-op.
207///
208/// [`Ref<T>`]: crate::Ref
209///
210/// # Examples
211///
212/// ```
213/// use musli_zerocopy::{endian, ZeroCopy};
214///
215/// #[derive(Debug, PartialEq, ZeroCopy)]
216/// #[repr(C)]
217/// struct Struct {
218///     c: char,
219///     bits32: u32,
220///     bits64: u64,
221/// }
222///
223/// let st = endian::from_le(Struct {
224///     c: 'a',
225///     bits32: 0x10203040u32.to_le(),
226///     bits64: 0x5060708090a0b0c0u64.to_le(),
227/// });
228///
229/// assert_eq!(st, Struct {
230///     c: 'a',
231///     bits32: 0x10203040,
232///     bits64: 0x5060708090a0b0c0,
233/// });
234/// ```
235#[inline]
236pub fn from_le<T>(value: T) -> T
237where
238    T: ZeroCopy,
239{
240    from_endian::<_, Little>(value)
241}
242
243/// Convert the value `T` from the specified [`ByteOrder`] `E` to [`Native`]
244/// endian.
245///
246/// This ignores types which has [`ZeroCopy::CAN_SWAP_BYTES`] set to `false`,
247/// such as [`char`]. Such values will simply pass through.
248///
249/// Swapping the bytes of a type which explicitly records its own byte order
250/// like [`Ref<T>`] is a no-op.
251///
252/// [`Ref<T>`]: crate::Ref
253///
254/// # Examples
255///
256/// ```
257/// use musli_zerocopy::{endian, ZeroCopy};
258///
259/// #[derive(Debug, PartialEq, ZeroCopy)]
260/// #[repr(C)]
261/// struct Struct {
262///     c: char,
263///     bits32: u32,
264///     bits64: u64,
265/// }
266///
267/// let st = endian::from_endian::<_, endian::Big>(Struct {
268///     c: 'a',
269///     bits32: 0x10203040u32.to_be(),
270///     bits64: 0x5060708090a0b0c0u64.to_be(),
271/// });
272///
273/// assert_eq!(st, Struct {
274///     c: 'a',
275///     bits32: 0x10203040,
276///     bits64: 0x5060708090a0b0c0,
277/// });
278/// ```
279#[inline]
280pub fn from_endian<T, E>(value: T) -> T
281where
282    T: ZeroCopy,
283    E: ByteOrder,
284{
285    value.swap_bytes::<E>().swap_bytes::<Native>()
286}
287
288mod sealed {
289    use super::{Big, Little};
290
291    pub trait Sealed {}
292
293    impl Sealed for Big {}
294    impl Sealed for Little {}
295}
296
297/// Defines a byte order to use.
298///
299/// This trait is implemented by two marker types [`Big`] and
300/// [`Little`], and its internals are intentionally hidden. Do not attempt
301/// to use them yourself.
302pub trait ByteOrder: 'static + Sized + self::sealed::Sealed {
303    /// Maps the `value` through `map`, unless the current byte order is
304    /// [`Native`].
305    #[doc(hidden)]
306    fn try_map<T, F>(value: T, map: F) -> T
307    where
308        F: FnOnce(T) -> T;
309
310    /// Swap the bytes for a `usize` with the current byte order.
311    #[doc(hidden)]
312    fn swap_usize(value: usize) -> usize;
313
314    /// Swap the bytes for a `isize` with the current byte order.
315    #[doc(hidden)]
316    fn swap_isize(value: isize) -> isize;
317
318    /// Swap the bytes of a `u16` with the current byte order.
319    #[doc(hidden)]
320    fn swap_u16(value: u16) -> u16;
321
322    /// Swap the bytes of a `i16` with the current byte order.
323    #[doc(hidden)]
324    fn swap_i16(value: i16) -> i16;
325
326    /// Swap the bytes for a `u32` with the current byte order.
327    #[doc(hidden)]
328    fn swap_u32(value: u32) -> u32;
329
330    /// Swap the bytes for a `i32` with the current byte order.
331    #[doc(hidden)]
332    fn swap_i32(value: i32) -> i32;
333
334    /// Swap the bytes for a `u64` with the current byte order.
335    #[doc(hidden)]
336    fn swap_u64(value: u64) -> u64;
337
338    /// Swap the bytes for a `i64` with the current byte order.
339    #[doc(hidden)]
340    fn swap_i64(value: i64) -> i64;
341
342    /// Swap the bytes for a `u128` with the current byte order.
343    #[doc(hidden)]
344    fn swap_u128(value: u128) -> u128;
345
346    /// Swap the bytes for a `i128` with the current byte order.
347    #[doc(hidden)]
348    fn swap_i128(value: i128) -> i128;
349
350    /// Swap the bytes for a `f32` with the current byte order.
351    #[doc(hidden)]
352    fn swap_f32(value: f32) -> f32;
353
354    /// Swap the bytes for a `f64` with the current byte order.
355    #[doc(hidden)]
356    fn swap_f64(value: f64) -> f64;
357}
358
359impl ByteOrder for Little {
360    #[cfg(target_endian = "little")]
361    #[inline(always)]
362    fn try_map<T, F>(value: T, _: F) -> T
363    where
364        F: FnOnce(T) -> T,
365    {
366        value
367    }
368
369    #[cfg(not(target_endian = "little"))]
370    #[inline(always)]
371    fn try_map<T, F>(value: T, map: F) -> T
372    where
373        F: FnOnce(T) -> T,
374    {
375        map(value)
376    }
377
378    #[inline]
379    fn swap_usize(value: usize) -> usize {
380        usize::from_le(value)
381    }
382
383    #[inline]
384    fn swap_isize(value: isize) -> isize {
385        isize::from_le(value)
386    }
387
388    #[inline]
389    fn swap_u16(value: u16) -> u16 {
390        u16::to_le(value)
391    }
392
393    #[inline]
394    fn swap_i16(value: i16) -> i16 {
395        i16::to_le(value)
396    }
397
398    #[inline]
399    fn swap_u32(value: u32) -> u32 {
400        u32::from_le(value)
401    }
402
403    #[inline]
404    fn swap_i32(value: i32) -> i32 {
405        i32::from_le(value)
406    }
407
408    #[inline]
409    fn swap_u64(value: u64) -> u64 {
410        u64::from_le(value)
411    }
412
413    #[inline]
414    fn swap_i64(value: i64) -> i64 {
415        i64::from_le(value)
416    }
417
418    #[inline]
419    fn swap_u128(value: u128) -> u128 {
420        u128::from_le(value)
421    }
422
423    #[inline]
424    fn swap_i128(value: i128) -> i128 {
425        i128::from_le(value)
426    }
427
428    #[inline]
429    fn swap_f32(value: f32) -> f32 {
430        f32::from_bits(u32::from_le(value.to_bits()))
431    }
432
433    #[inline]
434    fn swap_f64(value: f64) -> f64 {
435        f64::from_bits(u64::from_le(value.to_bits()))
436    }
437}
438
439impl ByteOrder for Big {
440    #[cfg(target_endian = "big")]
441    #[inline(always)]
442    fn try_map<T, F>(value: T, _: F) -> T
443    where
444        F: FnOnce(T) -> T,
445    {
446        value
447    }
448
449    #[cfg(not(target_endian = "big"))]
450    #[inline(always)]
451    fn try_map<T, F>(value: T, map: F) -> T
452    where
453        F: FnOnce(T) -> T,
454    {
455        map(value)
456    }
457
458    #[inline]
459    fn swap_usize(value: usize) -> usize {
460        usize::from_be(value)
461    }
462
463    #[inline]
464    fn swap_isize(value: isize) -> isize {
465        isize::from_be(value)
466    }
467
468    #[inline]
469    fn swap_u16(value: u16) -> u16 {
470        u16::to_be(value)
471    }
472
473    #[inline]
474    fn swap_i16(value: i16) -> i16 {
475        i16::to_be(value)
476    }
477
478    #[inline]
479    fn swap_u32(value: u32) -> u32 {
480        u32::from_be(value)
481    }
482
483    #[inline]
484    fn swap_i32(value: i32) -> i32 {
485        i32::from_be(value)
486    }
487
488    #[inline]
489    fn swap_u64(value: u64) -> u64 {
490        u64::from_be(value)
491    }
492
493    #[inline]
494    fn swap_i64(value: i64) -> i64 {
495        i64::from_be(value)
496    }
497
498    #[inline]
499    fn swap_u128(value: u128) -> u128 {
500        u128::from_be(value)
501    }
502
503    #[inline]
504    fn swap_i128(value: i128) -> i128 {
505        i128::from_be(value)
506    }
507
508    #[inline]
509    fn swap_f32(value: f32) -> f32 {
510        f32::from_bits(u32::from_be(value.to_bits()))
511    }
512
513    #[inline]
514    fn swap_f64(value: f64) -> f64 {
515        f64::from_bits(u64::from_be(value.to_bits()))
516    }
517}