nimble/
encode.rs

1use core::{
2    hash::BuildHasher,
3    marker::PhantomData,
4    num::{
5        NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
6        NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
7    },
8};
9use std::{
10    borrow::Cow,
11    collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque},
12    ffi::{CStr, CString},
13    sync::Arc,
14};
15
16use crate::{
17    async_trait,
18    io::{Write, WriteExt},
19    Config, Endianness, Result,
20};
21
22#[async_trait]
23/// Trait for encoding values
24pub trait Encode {
25    /// Returns size of encoded byte array
26    fn size(&self) -> usize;
27
28    /// Writes encoded byte array to writer and returns the number of bytes written
29    ///
30    /// ## Equivalent to:
31    ///
32    /// ```rust,ignore
33    /// async fn encode_to<W>(&self, writer: W) -> Result<usize>
34    /// where
35    ///     W: Write + Unpin + Send
36    /// ```
37    async fn encode_to<W>(&self, config: &Config, writer: W) -> Result<usize>
38    where
39        W: Write + Unpin + Send;
40}
41
42macro_rules! impl_primitive {
43    ($($type: tt),+) => {
44        $(
45            #[async_trait]
46            impl Encode for $type {
47                #[inline]
48                fn size(&self) -> usize {
49                    core::mem::size_of::<Self>()
50                }
51
52                async fn encode_to<W>(&self, config: &Config, mut writer: W) -> Result<usize>
53                where
54                    W: Write + Unpin + Send,
55                {
56                    match config.endianness {
57                        Endianness::LittleEndian => writer.write(&self.to_le_bytes()).await.map_err(Into::into),
58                        Endianness::BigEndian => writer.write(&self.to_be_bytes()).await.map_err(Into::into)
59                    }
60                }
61            }
62        )+
63    };
64}
65
66impl_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize, f32, f64);
67
68#[async_trait]
69impl Encode for bool {
70    #[inline]
71    fn size(&self) -> usize {
72        core::mem::size_of::<bool>()
73    }
74
75    #[allow(clippy::trivially_copy_pass_by_ref)]
76    async fn encode_to<W>(&self, config: &Config, writer: W) -> Result<usize>
77    where
78        W: Write + Unpin + Send,
79    {
80        (*self as u8).encode_to(config, writer).await
81    }
82}
83
84#[async_trait]
85impl Encode for char {
86    #[inline]
87    fn size(&self) -> usize {
88        core::mem::size_of::<char>()
89    }
90
91    #[allow(clippy::trivially_copy_pass_by_ref)]
92    async fn encode_to<W>(&self, config: &Config, writer: W) -> Result<usize>
93    where
94        W: Write + Unpin + Send,
95    {
96        (*self as u32).encode_to(config, writer).await
97    }
98}
99
100#[async_trait]
101impl<T> Encode for Option<T>
102where
103    T: Encode + Sync,
104{
105    fn size(&self) -> usize {
106        match self {
107            Some(ref value) => core::mem::size_of::<u8>() + value.size(),
108            None => core::mem::size_of::<u8>(),
109        }
110    }
111
112    async fn encode_to<W>(&self, config: &Config, mut writer: W) -> Result<usize>
113    where
114        W: Write + Unpin + Send,
115    {
116        match self {
117            None => 0u8.encode_to(config, &mut writer).await.map_err(Into::into),
118            Some(ref value) => Ok(1u8.encode_to(config, &mut writer).await?
119                + value.encode_to(config, &mut writer).await?),
120        }
121    }
122}
123
124#[async_trait]
125impl<T, E> Encode for core::result::Result<T, E>
126where
127    T: Encode + Sync,
128    E: Encode + Sync,
129{
130    fn size(&self) -> usize {
131        match self {
132            Ok(ref value) => core::mem::size_of::<u8>() + value.size(),
133            Err(ref err) => core::mem::size_of::<u8>() + err.size(),
134        }
135    }
136
137    async fn encode_to<W>(&self, config: &Config, mut writer: W) -> Result<usize>
138    where
139        W: Write + Unpin + Send,
140    {
141        match self {
142            Ok(ref value) => {
143                Ok(0u8.encode_to(config, &mut writer).await?
144                    + value.encode_to(config, writer).await?)
145            }
146            Err(ref err) => {
147                Ok(1u8.encode_to(config, &mut writer).await?
148                    + err.encode_to(config, writer).await?)
149            }
150        }
151    }
152}
153
154macro_rules! impl_seq {
155    ($ty: tt < T $(: $tbound1: tt $(+ $tbound2: ident)*)* $(, $typaram: tt : $bound1: tt $(+ $bound2: tt)*)* >) => {
156        #[async_trait]
157        impl<T $(, $typaram)*> Encode for $ty<T $(, $typaram)*>
158        where
159            T: Encode + Sync $(+ $tbound1 $(+ $tbound2)*)*,
160            $($typaram: $bound1 $(+ $bound2)*,)*
161        {
162            #[inline]
163            fn size(&self) -> usize {
164                core::mem::size_of::<u64>() + self.iter().map(Encode::size).sum::<usize>()
165            }
166
167            #[allow(clippy::ptr_arg)]
168            async fn encode_to<W>(&self, config: &Config, mut writer: W) -> Result<usize>
169            where
170                W: Write + Unpin + Send,
171            {
172                let mut encoded = 0;
173
174                encoded += (self.len() as u64).encode_to(config, &mut writer).await?;
175
176                for item in self.iter() {
177                    encoded += item.encode_to(config, &mut writer).await?;
178                }
179
180                Ok(encoded)
181            }
182        }
183    };
184}
185
186impl_seq!(Vec<T>);
187impl_seq!(VecDeque<T>);
188impl_seq!(LinkedList<T>);
189impl_seq!(HashSet<T, S: BuildHasher + Sync>);
190impl_seq!(BTreeSet<T: 'static>);
191impl_seq!(BinaryHeap<T>);
192
193#[async_trait]
194impl<T> Encode for [T]
195where
196    T: Encode + Sync,
197{
198    #[inline]
199    fn size(&self) -> usize {
200        core::mem::size_of::<u64>() + self.iter().map(Encode::size).sum::<usize>()
201    }
202
203    async fn encode_to<W>(&self, config: &Config, mut writer: W) -> Result<usize>
204    where
205        W: Write + Unpin + Send,
206    {
207        let mut encoded = 0;
208
209        encoded += (self.len() as u64).encode_to(config, &mut writer).await?;
210
211        for item in self.iter() {
212            encoded += item.encode_to(config, &mut writer).await?;
213        }
214
215        Ok(encoded)
216    }
217}
218
219macro_rules! impl_as_bytes {
220    ($ty: tt, $as_bytes: tt) => {
221        #[async_trait]
222        impl Encode for $ty {
223            #[inline]
224            fn size(&self) -> usize {
225                Self::$as_bytes(self).size()
226            }
227
228            #[allow(clippy::ptr_arg)]
229            async fn encode_to<W>(&self, config: &Config, writer: W) -> Result<usize>
230            where
231                W: Write + Unpin + Send,
232            {
233                Self::$as_bytes(self).encode_to(config, writer).await
234            }
235        }
236    };
237}
238
239impl_as_bytes!(str, as_bytes);
240impl_as_bytes!(String, as_bytes);
241impl_as_bytes!(CStr, to_bytes);
242impl_as_bytes!(CString, as_bytes);
243
244macro_rules! impl_deref {
245    ($($desc: tt)+) => {
246        #[async_trait]
247        impl $($desc)+ {
248            #[inline]
249            fn size(&self) -> usize {
250                <T>::size(self)
251            }
252
253            async fn encode_to<W>(&self, config: &Config, writer: W) -> Result<usize>
254            where
255                W: Write + Unpin + Send,
256            {
257                <T>::encode_to(self, config, writer).await
258            }
259        }
260    }
261}
262
263impl_deref!(<T: ?Sized> Encode for &T where T: Encode + Sync);
264impl_deref!(<T: ?Sized> Encode for &mut T where T: Encode + Sync);
265impl_deref!(<T: ?Sized> Encode for Box<T> where T: Encode + Sync);
266impl_deref!(<T: ?Sized> Encode for Arc<T> where T: Encode + Sync + Send);
267
268#[async_trait]
269impl<T: ?Sized> Encode for Cow<'_, T>
270where
271    T: Encode + ToOwned + Sync,
272    <T as ToOwned>::Owned: Sync,
273{
274    #[inline]
275    fn size(&self) -> usize {
276        self.as_ref().size()
277    }
278
279    #[allow(clippy::ptr_arg)]
280    async fn encode_to<W>(&self, config: &Config, writer: W) -> Result<usize>
281    where
282        W: Write + Unpin + Send,
283    {
284        self.as_ref().encode_to(config, writer).await
285    }
286}
287
288macro_rules! impl_map {
289    ($ty: tt < K $(: $kbound1: tt $(+ $kbound2: tt)*)*, V $(: $vbound1: tt $(+ $vbound2: tt)*)* $(, $typaram: tt : $bound1: tt $(+ $bound2: tt)*)* >) => {
290        #[async_trait]
291        impl<K, V $(, $typaram)*> Encode for $ty<K, V $(, $typaram)*>
292        where
293            K: Encode + Sync $(+ $kbound1 $(+ $kbound2)*)*,
294            V: Encode + Sync $(+ $vbound1 $(+ $vbound2)*)*,
295            $($typaram: $bound1 $(+ $bound2)*,)*
296        {
297            #[inline]
298            fn size(&self) -> usize {
299                core::mem::size_of::<u64>() + self.iter().map(|entry| entry.size()).sum::<usize>()
300            }
301
302            async fn encode_to<W>(&self, config: &Config, mut writer: W) -> Result<usize>
303            where
304                W: Write + Unpin + Send,
305            {
306                let mut encoded = 0;
307
308                encoded += (self.len() as u64).encode_to(config, &mut writer).await?;
309
310                for item in self.iter() {
311                    encoded += item.encode_to(config, &mut writer).await?;
312                }
313
314                Ok(encoded)
315            }
316        }
317    };
318}
319
320impl_map!(HashMap<K, V, S: BuildHasher + Sync>);
321impl_map!(BTreeMap<K: 'static, V: 'static>);
322
323macro_rules! impl_fixed_arr {
324    ($($len: tt),+) => {
325        $(
326            #[async_trait]
327            impl<T> Encode for [T; $len]
328            where
329                T: Encode + Sync,
330            {
331                #[inline]
332                fn size(&self) -> usize {
333                    self.iter().map(Encode::size).sum::<usize>()
334                }
335
336                async fn encode_to<W>(&self, config: &Config, mut writer: W) -> Result<usize>
337                where
338                    W: Write + Unpin + Send,
339                {
340                    let mut encoded = 0;
341
342                    for item in self.iter() {
343                        encoded += item.encode_to(config, &mut writer).await?;
344                    }
345
346                    Ok(encoded)
347                }
348            }
349        )+
350    };
351}
352
353impl_fixed_arr!(
354    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,
355    27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
356    51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 128, 256, 512, 1024
357);
358
359#[async_trait]
360impl Encode for () {
361    #[inline]
362    fn size(&self) -> usize {
363        0
364    }
365
366    #[allow(clippy::trivially_copy_pass_by_ref)]
367    async fn encode_to<W>(&self, _config: &Config, _writer: W) -> Result<usize>
368    where
369        W: Write + Unpin + Send,
370    {
371        Ok(0)
372    }
373}
374
375macro_rules! impl_tuple {
376    ($(($($n:tt $name:tt)+))+) => {
377        $(
378            #[async_trait]
379            impl<$($name),+> Encode for ($($name,)+)
380            where
381                $($name: Encode + Send + Sync,)+
382            {
383                #[inline]
384                fn size(&self) -> usize {
385                    0 $(+ self.$n.size())+
386                }
387
388                async fn encode_to<W>(&self, config: &Config, mut writer: W) -> Result<usize>
389                where
390                    W: Write + Unpin + Send,
391                {
392                    let mut encoded = 0;
393
394                    $(
395                        encoded += self.$n.encode_to(config, &mut writer).await?;
396                    )+
397
398                    Ok(encoded)
399                }
400            }
401        )+
402    }
403}
404
405impl_tuple! {
406    (0 T0)
407    (0 T0 1 T1)
408    (0 T0 1 T1 2 T2)
409    (0 T0 1 T1 2 T2 3 T3)
410    (0 T0 1 T1 2 T2 3 T3 4 T4)
411    (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5)
412    (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6)
413    (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7)
414    (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8)
415    (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9)
416    (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10)
417    (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11)
418    (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12)
419    (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13)
420    (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14)
421    (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15)
422}
423
424#[async_trait]
425impl<T> Encode for PhantomData<T>
426where
427    T: Send + Sync + ?Sized,
428{
429    #[inline]
430    fn size(&self) -> usize {
431        0
432    }
433
434    #[allow(clippy::trivially_copy_pass_by_ref)]
435    async fn encode_to<W>(&self, _config: &Config, _writer: W) -> Result<usize>
436    where
437        W: Write + Unpin + Send,
438    {
439        Ok(0)
440    }
441}
442
443macro_rules! impl_non_zero_primitives {
444    ($($type: tt),+) => {
445        $(
446            #[async_trait]
447            impl Encode for $type {
448                #[inline]
449                fn size(&self) -> usize {
450                    core::mem::size_of::<Self>()
451                }
452
453                async fn encode_to<W>(&self, config: &Config, writer: W) -> Result<usize>
454                where
455                    W: Write + Unpin + Send,
456                {
457                    self.get().encode_to(config, writer).await
458                }
459            }
460        )+
461    };
462}
463
464impl_non_zero_primitives!(
465    NonZeroU8,
466    NonZeroU16,
467    NonZeroU32,
468    NonZeroU64,
469    NonZeroU128,
470    NonZeroI8,
471    NonZeroI16,
472    NonZeroI32,
473    NonZeroI64,
474    NonZeroI128,
475    NonZeroUsize,
476    NonZeroIsize
477);