1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use core::fmt;

use crate::endian::ByteOrder;
use crate::error::IntoRepr;
use crate::traits::ZeroCopy;

/// The default [`Size`] to use.
pub type DefaultSize = u32;

#[cfg(not(any(
    target_pointer_width = "32",
    target_pointer_width = "64",
    target_pointer_width = "128"
)))]
compile_error!("musli-zerocopy is only supported on 32, 64, or 128-bit platforms");

mod sealed {
    pub trait Sealed {}
    impl Sealed for u8 {}
    impl Sealed for u16 {}
    impl Sealed for u32 {}
    #[cfg(any(target_pointer_width = "64", target_pointer_width = "128"))]
    impl Sealed for u64 {}
    impl Sealed for usize {}
}

/// Trait which defines the size of a pointer.
///
/// Some of the available [`Size`] implementations are:
/// * `u8`, `u16`, and `u32` for sized pointers matching the width of the
///   specified type.
/// * `usize` for target-dependently sized pointers.
///
/// The default size is defined by the [`DefaultSize`] type alias.
///
/// This trait is sealed and its internals hidden. Publicly it's only used as a
/// marker trait.
pub trait Size:
    'static
    + Sized
    + TryFrom<usize>
    + Copy
    + fmt::Display
    + fmt::Debug
    + ZeroCopy
    + IntoRepr
    + self::sealed::Sealed
{
    /// The default zero pointer.
    #[doc(hidden)]
    const ZERO: Self;

    /// The max size of a pointer.
    #[doc(hidden)]
    const MAX: Self;

    #[doc(hidden)]
    const ONE: Self;

    #[doc(hidden)]
    const N2: Self;

    #[doc(hidden)]
    const N4: Self;

    #[doc(hidden)]
    const N8: Self;

    #[doc(hidden)]
    const N16: Self;

    #[doc(hidden)]
    /// Perform wrapping multiplication over the type.
    fn wrapping_mul(self, other: Self) -> Self;

    #[doc(hidden)]
    /// Perform checked multiplication over the type.
    fn checked_mul(self, other: Self) -> Option<Self>;

    /// Try to construct this value from usize.
    fn try_from_usize(value: usize) -> Option<Self>;

    /// Convert the pointer to a usize.
    #[doc(hidden)]
    fn as_usize<E: ByteOrder>(self) -> usize;

    /// Test if the value is zero.
    #[doc(hidden)]
    fn is_zero(self) -> bool;
}

macro_rules! impl_size {
    ($ty:ty, $swap:path) => {
        #[doc = concat!("Size implementation for `", stringify!($ty), "`")]
        ///
        /// # Examples
        ///
        /// ```
        /// use musli_zerocopy::{endian, Size};
        ///
        #[doc = concat!("let max = ", stringify!($ty), "::MAX.as_usize::<endian::Big>();")]
        #[doc = concat!("let min = ", stringify!($ty), "::MIN.as_usize::<endian::Little>();")]
        /// ```
        impl Size for $ty {
            const ZERO: Self = 0;
            const MAX: Self = <$ty>::MAX;
            const ONE: Self = 1;
            const N2: Self = 2;
            const N4: Self = 4;
            const N8: Self = 8;
            const N16: Self = 16;

            #[inline(always)]
            fn wrapping_mul(self, other: Self) -> Self {
                self.wrapping_mul(other)
            }

            #[inline(always)]
            fn checked_mul(self, other: Self) -> Option<Self> {
                self.checked_mul(other)
            }

            #[inline]
            fn try_from_usize(value: usize) -> Option<Self> {
                if value > <$ty>::MAX as usize {
                    None
                } else {
                    Some(value as $ty)
                }
            }

            #[inline]
            fn as_usize<E: ByteOrder>(self) -> usize {
                if self > usize::MAX as $ty {
                    usize::MAX
                } else {
                    $swap(self) as usize
                }
            }

            #[inline]
            fn is_zero(self) -> bool {
                self == 0
            }
        }
    };
}

impl_size!(u8, core::convert::identity);
impl_size!(u16, E::swap_u16);
impl_size!(u32, E::swap_u32);
#[cfg(any(target_pointer_width = "64", target_pointer_width = "128"))]
impl_size!(u64, E::swap_u64);
impl_size!(usize, core::convert::identity);