Skip to main content

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