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#[doc(inline)]
134pub use self::endian::Endian;
135mod endian;
136
137/// Alias for the native endian [`ByteOrder`].
138#[cfg(target_endian = "little")]
139pub type Native = Little;
140
141/// Alias for the native endian [`ByteOrder`].
142#[cfg(target_endian = "big")]
143pub type Native = Big;
144
145/// Alias for the opposite endian [`ByteOrder`].
146#[cfg(target_endian = "little")]
147pub type Other = Big;
148
149/// Alias for the opposite endian [`ByteOrder`].
150#[cfg(target_endian = "big")]
151pub type Other = Little;
152
153/// Marker type indicating that the big endian [`ByteOrder`] is in use.
154#[non_exhaustive]
155pub struct Big;
156
157/// Marker type indicating that the little endian [`ByteOrder`] is in use.
158#[non_exhaustive]
159pub struct Little;
160
161use crate::ZeroCopy;
162
163/// Convert the value `T` from [`Big`] to [`Native`] endian.
164///
165/// This ignores types which has [`ZeroCopy::CAN_SWAP_BYTES`] set to `false`,
166/// such as [`char`]. Such values will simply pass through.
167///
168/// Swapping the bytes of a type which explicitly records its own byte order
169/// like [`Ref<T>`] is a no-op.
170///
171/// [`Ref<T>`]: crate::Ref
172///
173/// # Examples
174///
175/// ```
176/// use musli_zerocopy::{endian, ZeroCopy};
177///
178/// #[derive(Debug, PartialEq, ZeroCopy)]
179/// #[repr(C)]
180/// struct Struct {
181///     c: char,
182///     bits32: u32,
183///     bits64: u64,
184/// }
185///
186/// let st = endian::from_be(Struct {
187///     c: 'a',
188///     bits32: 0x10203040u32.to_be(),
189///     bits64: 0x5060708090a0b0c0u64.to_be(),
190/// });
191///
192/// assert_eq!(st, Struct {
193///     c: 'a',
194///     bits32: 0x10203040,
195///     bits64: 0x5060708090a0b0c0,
196/// });
197/// ```
198pub fn from_be<T>(value: T) -> T
199where
200    T: ZeroCopy,
201{
202    from_endian::<_, Big>(value)
203}
204
205/// Convert the value `T` from [`Little`] to [`Native`] endian.
206///
207/// This ignores types which has [`ZeroCopy::CAN_SWAP_BYTES`] set to `false`,
208/// such as [`char`]. Such values will simply pass through.
209///
210/// Swapping the bytes of a type which explicitly records its own byte order
211/// like [`Ref<T>`] is a no-op.
212///
213/// [`Ref<T>`]: crate::Ref
214///
215/// # Examples
216///
217/// ```
218/// use musli_zerocopy::{endian, ZeroCopy};
219///
220/// #[derive(Debug, PartialEq, ZeroCopy)]
221/// #[repr(C)]
222/// struct Struct {
223///     c: char,
224///     bits32: u32,
225///     bits64: u64,
226/// }
227///
228/// let st = endian::from_le(Struct {
229///     c: 'a',
230///     bits32: 0x10203040u32.to_le(),
231///     bits64: 0x5060708090a0b0c0u64.to_le(),
232/// });
233///
234/// assert_eq!(st, Struct {
235///     c: 'a',
236///     bits32: 0x10203040,
237///     bits64: 0x5060708090a0b0c0,
238/// });
239/// ```
240#[inline]
241pub fn from_le<T>(value: T) -> T
242where
243    T: ZeroCopy,
244{
245    from_endian::<_, Little>(value)
246}
247
248/// Convert the value `T` from the specified [`ByteOrder`] `E` to [`Native`]
249/// endian.
250///
251/// This ignores types which has [`ZeroCopy::CAN_SWAP_BYTES`] set to `false`,
252/// such as [`char`]. Such values will simply pass through.
253///
254/// Swapping the bytes of a type which explicitly records its own byte order
255/// like [`Ref<T>`] is a no-op.
256///
257/// [`Ref<T>`]: crate::Ref
258///
259/// # Examples
260///
261/// ```
262/// use musli_zerocopy::{endian, ZeroCopy};
263///
264/// #[derive(Debug, PartialEq, ZeroCopy)]
265/// #[repr(C)]
266/// struct Struct {
267///     c: char,
268///     bits32: u32,
269///     bits64: u64,
270/// }
271///
272/// let st = endian::from_endian::<_, endian::Big>(Struct {
273///     c: 'a',
274///     bits32: 0x10203040u32.to_be(),
275///     bits64: 0x5060708090a0b0c0u64.to_be(),
276/// });
277///
278/// assert_eq!(st, Struct {
279///     c: 'a',
280///     bits32: 0x10203040,
281///     bits64: 0x5060708090a0b0c0,
282/// });
283/// ```
284#[inline]
285pub fn from_endian<T, E>(value: T) -> T
286where
287    T: ZeroCopy,
288    E: ByteOrder,
289{
290    value.swap_bytes::<E>().swap_bytes::<Native>()
291}
292
293mod sealed {
294    use super::{Big, Little};
295
296    pub trait Sealed {}
297
298    impl Sealed for Big {}
299    impl Sealed for Little {}
300}
301
302/// Defines a byte order to use.
303///
304/// This trait is implemented by two marker types [`Big`] and
305/// [`Little`], and its internals are intentionally hidden. Do not attempt
306/// to use them yourself.
307pub trait ByteOrder
308where
309    Self: 'static + Sized + self::sealed::Sealed,
310{
311    /// The name of the byte order for debugging purposes.
312    #[doc(hidden)]
313    const NAME: &'static str;
314
315    /// Maps the `value` through `map`, unless the current byte order is
316    /// [`Native`].
317    #[doc(hidden)]
318    fn try_map<T, F>(value: T, map: F) -> T
319    where
320        F: FnOnce(T) -> T;
321
322    /// Swap the bytes for a `usize` with the current byte order.
323    #[doc(hidden)]
324    fn swap_usize(value: usize) -> usize;
325
326    /// Swap the bytes for a `isize` with the current byte order.
327    #[doc(hidden)]
328    fn swap_isize(value: isize) -> isize;
329
330    /// Swap the bytes of a `u16` with the current byte order.
331    #[doc(hidden)]
332    fn swap_u16(value: u16) -> u16;
333
334    /// Swap the bytes of a `i16` with the current byte order.
335    #[doc(hidden)]
336    fn swap_i16(value: i16) -> i16;
337
338    /// Swap the bytes for a `u32` with the current byte order.
339    #[doc(hidden)]
340    fn swap_u32(value: u32) -> u32;
341
342    /// Swap the bytes for a `i32` with the current byte order.
343    #[doc(hidden)]
344    fn swap_i32(value: i32) -> i32;
345
346    /// Swap the bytes for a `u64` with the current byte order.
347    #[doc(hidden)]
348    fn swap_u64(value: u64) -> u64;
349
350    /// Swap the bytes for a `i64` with the current byte order.
351    #[doc(hidden)]
352    fn swap_i64(value: i64) -> i64;
353
354    /// Swap the bytes for a `u128` with the current byte order.
355    #[doc(hidden)]
356    fn swap_u128(value: u128) -> u128;
357
358    /// Swap the bytes for a `i128` with the current byte order.
359    #[doc(hidden)]
360    fn swap_i128(value: i128) -> i128;
361
362    /// Swap the bytes for a `f32` with the current byte order.
363    #[doc(hidden)]
364    fn swap_f32(value: f32) -> f32;
365
366    /// Swap the bytes for a `f64` with the current byte order.
367    #[doc(hidden)]
368    fn swap_f64(value: f64) -> f64;
369}
370
371impl ByteOrder for Little {
372    #[cfg(target_endian = "little")]
373    const NAME: &'static str = "Native (Little)";
374
375    #[cfg(not(target_endian = "little"))]
376    const NAME: &'static str = "Little";
377
378    #[cfg(target_endian = "little")]
379    #[inline(always)]
380    fn try_map<T, F>(value: T, _: F) -> T
381    where
382        F: FnOnce(T) -> T,
383    {
384        value
385    }
386
387    #[cfg(not(target_endian = "little"))]
388    #[inline(always)]
389    fn try_map<T, F>(value: T, map: F) -> T
390    where
391        F: FnOnce(T) -> T,
392    {
393        map(value)
394    }
395
396    #[inline]
397    fn swap_usize(value: usize) -> usize {
398        usize::from_le(value)
399    }
400
401    #[inline]
402    fn swap_isize(value: isize) -> isize {
403        isize::from_le(value)
404    }
405
406    #[inline]
407    fn swap_u16(value: u16) -> u16 {
408        u16::to_le(value)
409    }
410
411    #[inline]
412    fn swap_i16(value: i16) -> i16 {
413        i16::to_le(value)
414    }
415
416    #[inline]
417    fn swap_u32(value: u32) -> u32 {
418        u32::from_le(value)
419    }
420
421    #[inline]
422    fn swap_i32(value: i32) -> i32 {
423        i32::from_le(value)
424    }
425
426    #[inline]
427    fn swap_u64(value: u64) -> u64 {
428        u64::from_le(value)
429    }
430
431    #[inline]
432    fn swap_i64(value: i64) -> i64 {
433        i64::from_le(value)
434    }
435
436    #[inline]
437    fn swap_u128(value: u128) -> u128 {
438        u128::from_le(value)
439    }
440
441    #[inline]
442    fn swap_i128(value: i128) -> i128 {
443        i128::from_le(value)
444    }
445
446    #[inline]
447    fn swap_f32(value: f32) -> f32 {
448        f32::from_bits(u32::from_le(value.to_bits()))
449    }
450
451    #[inline]
452    fn swap_f64(value: f64) -> f64 {
453        f64::from_bits(u64::from_le(value.to_bits()))
454    }
455}
456
457impl ByteOrder for Big {
458    #[cfg(target_endian = "big")]
459    const NAME: &'static str = "Native (Big)";
460
461    #[cfg(not(target_endian = "big"))]
462    const NAME: &'static str = "Big";
463
464    #[cfg(target_endian = "big")]
465    #[inline(always)]
466    fn try_map<T, F>(value: T, _: F) -> T
467    where
468        F: FnOnce(T) -> T,
469    {
470        value
471    }
472
473    #[cfg(not(target_endian = "big"))]
474    #[inline(always)]
475    fn try_map<T, F>(value: T, map: F) -> T
476    where
477        F: FnOnce(T) -> T,
478    {
479        map(value)
480    }
481
482    #[inline]
483    fn swap_usize(value: usize) -> usize {
484        usize::from_be(value)
485    }
486
487    #[inline]
488    fn swap_isize(value: isize) -> isize {
489        isize::from_be(value)
490    }
491
492    #[inline]
493    fn swap_u16(value: u16) -> u16 {
494        u16::to_be(value)
495    }
496
497    #[inline]
498    fn swap_i16(value: i16) -> i16 {
499        i16::to_be(value)
500    }
501
502    #[inline]
503    fn swap_u32(value: u32) -> u32 {
504        u32::from_be(value)
505    }
506
507    #[inline]
508    fn swap_i32(value: i32) -> i32 {
509        i32::from_be(value)
510    }
511
512    #[inline]
513    fn swap_u64(value: u64) -> u64 {
514        u64::from_be(value)
515    }
516
517    #[inline]
518    fn swap_i64(value: i64) -> i64 {
519        i64::from_be(value)
520    }
521
522    #[inline]
523    fn swap_u128(value: u128) -> u128 {
524        u128::from_be(value)
525    }
526
527    #[inline]
528    fn swap_i128(value: i128) -> i128 {
529        i128::from_be(value)
530    }
531
532    #[inline]
533    fn swap_f32(value: f32) -> f32 {
534        f32::from_bits(u32::from_be(value.to_bits()))
535    }
536
537    #[inline]
538    fn swap_f64(value: f64) -> f64 {
539        f64::from_bits(u64::from_be(value.to_bits()))
540    }
541}