musli_zerocopy/endian/endian.rs
1use core::marker::PhantomData;
2use core::ops::{Deref, DerefMut};
3use core::{any, fmt};
4
5use crate::ZeroCopy;
6use crate::endian::{Big, ByteOrder, Little, Native};
7
8/// Wrapper capable of enforcing a custom [`ByteOrder`].
9///
10/// This can be used to store values in a zero-copy container in a portable
11/// manner, which is especially important to transfer types such as `char` which
12/// have a limited supported bit-pattern.
13///
14/// # Wrapping fields
15///
16/// Any type which implements [`ZeroCopy`] and has [`ZeroCopy::CAN_SWAP_BYTES`]
17/// set to `true` can be portably wrapped with this type.
18///
19/// [`ZeroCopy::CAN_SWAP_BYTES`] is not `true` it indicates that the type
20/// contains data which cannot be safely byte-swapped, such as [`char`]. Byte
21/// swapping such a type is a no-op and will return the original type.
22///
23/// ```
24/// use musli_zerocopy::{endian, Endian, OwnedBuf, Ref, ZeroCopy};
25///
26/// #[derive(Clone, Copy, ZeroCopy)]
27/// #[repr(C)]
28/// struct Struct {
29/// name: Ref<str>,
30/// age: Endian<u32, endian::Big>,
31/// }
32///
33/// let mut buf = OwnedBuf::new();
34///
35/// let name = buf.store_unsized("John");
36///
37/// let data = buf.store(&Struct {
38/// name,
39/// age: Endian::new(35),
40/// });
41///
42/// buf.align_in_place();
43///
44/// let data = buf.load(data)?;
45///
46/// assert_eq!(buf.load(data.name)?, "John");
47/// assert_eq!(data.age.to_ne(), 35);
48/// # Ok::<_, musli_zerocopy::Error>(())
49/// ```
50#[derive(ZeroCopy)]
51#[zero_copy(crate, swap_bytes_self, bounds = {T: ZeroCopy})]
52#[repr(transparent)]
53pub struct Endian<T, E>
54where
55 E: ByteOrder,
56{
57 value: T,
58 #[zero_copy(ignore)]
59 _marker: PhantomData<E>,
60}
61
62impl<T> Endian<T, Little>
63where
64 T: ZeroCopy,
65{
66 /// Construct new value wrapper with [`Little`] [`ByteOrder`].
67 ///
68 /// # Examples
69 ///
70 /// ```
71 /// use musli_zerocopy::Endian;
72 ///
73 /// let value = Endian::le(42u32);
74 /// assert_eq!(value.to_ne(), 42);
75 /// assert_eq!(value.to_raw(), 42u32.to_le());
76 /// ```
77 #[inline]
78 pub fn le(value: T) -> Self {
79 Self::new(value)
80 }
81}
82
83impl<T> Endian<T, Big>
84where
85 T: ZeroCopy,
86{
87 /// Construct new value wrapper with [`Big`] [`ByteOrder`].
88 ///
89 /// # Examples
90 ///
91 /// ```
92 /// use musli_zerocopy::Endian;
93 ///
94 /// let value = Endian::be(42u32);
95 /// assert_eq!(value.to_ne(), 42);
96 /// assert_eq!(value.to_raw(), 42u32.to_be());
97 /// ```
98 #[inline]
99 pub fn be(value: T) -> Self {
100 Self::new(value)
101 }
102}
103
104impl<T, E> Endian<T, E>
105where
106 T: ZeroCopy,
107 E: ByteOrder,
108{
109 /// Construct new value wrapper with the specified [`ByteOrder`].
110 ///
111 /// # Panics
112 ///
113 /// Panics if we try to use this with a `ZeroCopy` type that cannot be
114 /// byte-ordered.
115 ///
116 /// ```compile_fail
117 /// use musli_zerocopy::{endian, Endian};
118 ///
119 /// let _: Endian<_, endian::Little> = Endian::new('a');
120 /// ```
121 ///
122 /// # Examples
123 ///
124 /// ```
125 /// use musli_zerocopy::{endian, Endian, ZeroCopy};
126 ///
127 /// let mut a: Endian<_, endian::Big> = Endian::new('a' as u32);
128 /// let mut b: Endian<_, endian::Little> = Endian::new('a' as u32);
129 ///
130 /// assert_eq!(a.to_ne(), 'a' as u32);
131 /// assert_eq!(a.to_bytes(), &[0, 0, 0, 97]);
132 ///
133 /// assert_eq!(b.to_ne(), 'a' as u32);
134 /// assert_eq!(b.to_bytes(), &[97, 0, 0, 0]);
135 /// ```
136 #[inline]
137 #[track_caller]
138 pub fn new(value: T) -> Self {
139 const {
140 assert!(T::CAN_SWAP_BYTES, "Type does not support byte-swapping");
141 }
142
143 Self {
144 value: T::swap_bytes::<E>(value),
145 _marker: PhantomData,
146 }
147 }
148
149 /// Get interior value in native [`ByteOrder`].
150 ///
151 /// # Examples
152 ///
153 /// ```
154 /// use musli_zerocopy::Endian;
155 ///
156 /// let value = Endian::le(42u32);
157 /// assert_eq!(value.to_ne(), 42);
158 /// assert_eq!(value.to_raw(), 42u32.to_le());
159 /// ```
160 #[inline]
161 #[track_caller]
162 pub fn to_ne(self) -> T {
163 const {
164 assert!(T::CAN_SWAP_BYTES, "Type does not support byte-swapping");
165 }
166
167 T::swap_bytes::<E>(self.value)
168 }
169
170 /// Get the raw inner value.
171 ///
172 /// # Examples
173 ///
174 /// ```
175 /// use musli_zerocopy::Endian;
176 ///
177 /// let value = Endian::le(42u32);
178 /// assert_eq!(value.to_ne(), 42);
179 /// assert_eq!(value.to_raw(), 42u32.to_le());
180 /// ```
181 #[inline]
182 pub fn to_raw(self) -> T {
183 self.value
184 }
185}
186
187impl<T, E> fmt::Debug for Endian<T, E>
188where
189 T: ZeroCopy + fmt::Debug,
190 E: ByteOrder,
191{
192 #[inline]
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 write!(f, "Endian<{}>({:?})", any::type_name::<E>(), self.value)
195 }
196}
197
198impl<T, E> Clone for Endian<T, E>
199where
200 T: ZeroCopy + Clone,
201 E: ByteOrder,
202{
203 fn clone(&self) -> Self {
204 Self {
205 value: self.value.clone(),
206 _marker: self._marker,
207 }
208 }
209}
210
211impl<T, E> Copy for Endian<T, E>
212where
213 T: ZeroCopy + Copy,
214 E: ByteOrder,
215{
216}
217
218/// Any `Endian<T>` implements [`Deref<Target = T>`] for natively wrapped types.
219///
220/// # Examples
221///
222/// ```
223/// use musli_zerocopy::Endian;
224///
225/// let value = Endian::new(42u32);
226/// assert_eq!(*value, 42u32);
227/// ```
228impl<T> Deref for Endian<T, Native> {
229 type Target = T;
230
231 #[inline]
232 fn deref(&self) -> &Self::Target {
233 &self.value
234 }
235}
236
237/// Any `Endian<T>` implements [`DerefMut<Target = T>`] for natively wrapped types.
238///
239/// # Examples
240///
241/// ```
242/// use musli_zerocopy::Endian;
243///
244/// let mut value = Endian::new(42u32);
245/// assert_eq!(*value, 42u32);
246/// *value += 1;
247/// assert_eq!(*value, 43u32);
248/// ```
249impl<T> DerefMut for Endian<T, Native> {
250 #[inline]
251 fn deref_mut(&mut self) -> &mut Self::Target {
252 &mut self.value
253 }
254}