Skip to main content

commonware_codec/
codec.rs

1//! Core traits for encoding and decoding.
2
3use crate::error::Error;
4#[cfg(not(feature = "std"))]
5use alloc::{sync::Arc, vec::Vec};
6use bytes::{Buf, BufMut, Bytes, BytesMut};
7#[cfg(feature = "std")]
8use std::{sync::Arc, vec::Vec};
9
10/// Trait for types with a known, fixed encoded size.
11///
12/// Implementing this trait signifies that the encoded representation of this type *always* has the
13/// same byte length, regardless of the specific value.
14///
15/// This automatically provides an implementation of [EncodeSize].
16pub trait FixedSize {
17    /// The size of the encoded value (in bytes).
18    const SIZE: usize;
19}
20
21/// Trait for types that can provide their encoded size in bytes.
22///
23/// This must be implemented by all encodable types. For types implementing [FixedSize], this
24/// trait is implemented automatically. For variable-size types, this requires calculating the size
25/// based on the value.
26pub trait EncodeSize {
27    /// Returns the encoded size of this value (in bytes).
28    fn encode_size(&self) -> usize;
29
30    /// Returns the total encoded size of a sequence, excluding any container-specific length
31    /// prefix.
32    ///
33    /// Container implementations call this hook so element types can provide a more efficient
34    /// aggregate size calculation. The default preserves normal element-by-element sizing.
35    /// Fixed-size implementations compute `SIZE * len`, which avoids an O(n) sizing prepass
36    /// before containers such as `Vec<T>` allocate their output buffer.
37    ///
38    /// This hook exists because stable Rust cannot express the overlapping specialized
39    /// container impls that would otherwise provide this aggregate path directly.
40    ///
41    /// This is hidden from generated documentation because it is an implementation hook for
42    /// codec's container types, not part of the intended user-facing API. Most users should
43    /// implement [EncodeSize::encode_size] or [FixedSize] instead.
44    #[doc(hidden)]
45    #[inline]
46    fn encode_size_slice(values: &[Self]) -> usize
47    where
48        Self: Sized,
49    {
50        values.iter().map(EncodeSize::encode_size).sum()
51    }
52
53    /// Returns the encoded size excluding bytes passed to [`BufsMut::push`]
54    /// during [`Write::write_bufs`]. Used to size the working buffer for inline
55    /// writes. Override alongside [`Write::write_bufs`] for types where large
56    /// [`Bytes`] fields go via push; failing to do so will over-allocate.
57    #[inline]
58    fn encode_inline_size(&self) -> usize {
59        self.encode_size()
60    }
61
62    /// Returns the total inline encoded size of a sequence, excluding any container-specific
63    /// length prefix.
64    ///
65    /// This hidden hook is the slice equivalent of [EncodeSize::encode_inline_size]. The
66    /// default preserves normal element-by-element sizing. Fixed-size implementations override
67    /// this to compute `SIZE * len`, matching [EncodeSize::encode_size_slice] for the
68    /// [`Write::write_bufs`] path.
69    ///
70    /// This hook exists because stable Rust cannot express the overlapping specialized
71    /// container impls that would otherwise provide this aggregate path directly.
72    ///
73    /// This is hidden from generated documentation for the same reason as
74    /// [EncodeSize::encode_size_slice].
75    #[doc(hidden)]
76    #[inline]
77    fn encode_inline_size_slice(values: &[Self]) -> usize
78    where
79        Self: Sized,
80    {
81        values
82            .iter()
83            .map(EncodeSize::encode_inline_size)
84            .sum::<usize>()
85    }
86}
87
88// Automatically implement `EncodeSize` for types that are `FixedSize`.
89impl<T: FixedSize> EncodeSize for T {
90    #[inline]
91    fn encode_size(&self) -> usize {
92        Self::SIZE
93    }
94
95    #[inline]
96    fn encode_size_slice(values: &[Self]) -> usize
97    where
98        Self: Sized,
99    {
100        Self::SIZE * values.len()
101    }
102
103    #[inline]
104    fn encode_inline_size_slice(values: &[Self]) -> usize
105    where
106        Self: Sized,
107    {
108        Self::encode_size_slice(values)
109    }
110}
111
112/// Trait for types that can be written (encoded) to a byte buffer.
113pub trait Write {
114    /// Writes the binary representation of `self` to the provided buffer `buf`.
115    ///
116    /// Implementations should panic if the buffer doesn't have enough capacity.
117    fn write(&self, buf: &mut impl BufMut);
118
119    /// Writes the encoded payload for a sequence, excluding any container-specific length
120    /// prefix.
121    ///
122    /// Container implementations call this hook so element types can provide a more efficient
123    /// aggregate write path. The default preserves normal element-by-element encoding.
124    ///
125    /// This hook exists because stable Rust cannot express the overlapping specialized
126    /// container impls that would otherwise provide this aggregate path directly.
127    ///
128    /// This is hidden from generated documentation because it is an implementation hook for
129    /// codec's container types, not part of the intended user-facing API. Most users should
130    /// implement [Write::write] instead.
131    #[doc(hidden)]
132    #[inline]
133    fn write_slice(values: &[Self], buf: &mut impl BufMut)
134    where
135        Self: Sized,
136    {
137        for item in values {
138            item.write(buf);
139        }
140    }
141
142    /// Writes to a [`BufsMut`], allowing existing [`Bytes`] chunks to be
143    /// appended via [`BufsMut::push`] instead of written inline. Must encode
144    /// to the same format as [`Write::write`]. Defaults to [`Write::write`].
145    #[inline]
146    fn write_bufs(&self, buf: &mut impl BufsMut) {
147        self.write(buf);
148    }
149
150    /// Writes the encoded payload for a sequence to a [`BufsMut`], excluding any
151    /// container-specific length prefix.
152    ///
153    /// This hidden hook is the slice equivalent of [Write::write_bufs]. The default preserves
154    /// normal element-by-element encoding.
155    ///
156    /// This hook exists because stable Rust cannot express the overlapping specialized
157    /// container impls that would otherwise provide this aggregate path directly.
158    ///
159    /// This is hidden from generated documentation for the same reason as [Write::write_slice].
160    #[doc(hidden)]
161    #[inline]
162    fn write_slice_bufs(values: &[Self], buf: &mut impl BufsMut)
163    where
164        Self: Sized,
165    {
166        for item in values {
167            item.write_bufs(buf);
168        }
169    }
170}
171
172impl<T: EncodeSize + ?Sized> EncodeSize for Arc<T> {
173    #[inline]
174    fn encode_size(&self) -> usize {
175        self.as_ref().encode_size()
176    }
177
178    #[inline]
179    fn encode_inline_size(&self) -> usize {
180        self.as_ref().encode_inline_size()
181    }
182}
183
184impl<T: Write + ?Sized> Write for Arc<T> {
185    #[inline]
186    fn write(&self, buf: &mut impl BufMut) {
187        self.as_ref().write(buf);
188    }
189
190    #[inline]
191    fn write_bufs(&self, buf: &mut impl BufsMut) {
192        self.as_ref().write_bufs(buf);
193    }
194}
195
196/// Trait for types that can be read (decoded) from a byte buffer.
197pub trait Read: Sized {
198    /// The `Cfg` type parameter allows passing configuration during the read process. This is
199    /// crucial for safely decoding untrusted data, for example, by providing size limits for
200    /// collections or strings.
201    ///
202    /// Use `Cfg = ()` if no configuration is needed for a specific type.
203    type Cfg: Clone + Send + Sync + 'static;
204
205    /// Reads a value from the buffer using the provided configuration `cfg`.
206    ///
207    /// Implementations should consume the exact number of bytes required from `buf` to reconstruct
208    /// the value.
209    ///
210    /// Implementations must return [Error] if decoding fails due to invalid data, insufficient
211    /// bytes in the buffer, or violation of constraints imposed by the `cfg`.
212    ///
213    /// # Warning
214    ///
215    /// Parsing a message (often untrusted) should never result in a panic.
216    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error>;
217
218    /// Reads `len` values from the buffer into a vector.
219    ///
220    /// Container implementations call this hook so element types can provide a more efficient
221    /// vector read path. The default preserves normal element-by-element decoding.
222    ///
223    /// This hook exists because stable Rust cannot express the overlapping specialized
224    /// container impls that would otherwise provide this aggregate path directly.
225    ///
226    /// This is hidden from generated documentation because it is an implementation hook for
227    /// codec's container types, not part of the intended user-facing API. Most users should
228    /// implement [Read::read_cfg] instead.
229    #[doc(hidden)]
230    #[inline]
231    fn read_vec(buf: &mut impl Buf, len: usize, cfg: &Self::Cfg) -> Result<Vec<Self>, Error> {
232        let mut values = Vec::with_capacity(len.min(buf.remaining()));
233        for _ in 0..len {
234            values.push(Self::read_cfg(buf, cfg)?);
235        }
236        Ok(values)
237    }
238
239    /// Reads exactly `N` values from the buffer into an array.
240    ///
241    /// This hidden hook is the array equivalent of [Read::read_vec]. The default preserves
242    /// normal element-by-element decoding.
243    ///
244    /// This hook exists because stable Rust cannot express the overlapping specialized array
245    /// impls that would otherwise provide this aggregate path directly.
246    ///
247    /// This is hidden from generated documentation for the same reason as [Read::read_vec].
248    #[doc(hidden)]
249    #[inline]
250    fn read_array<const N: usize>(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<[Self; N], Error> {
251        Ok(Self::read_vec(buf, N, cfg)?
252            .try_into()
253            .unwrap_or_else(|_| unreachable!("array length should match capacity")))
254    }
255}
256
257/// Trait combining [Write] and [EncodeSize] for types that can be fully encoded.
258///
259/// This trait provides the convenience [Encode::encode] method which handles
260/// buffer allocation, writing, and size assertion in one go.
261pub trait Encode: Write + EncodeSize {
262    /// Encodes `self` into a new [Bytes] buffer.
263    ///
264    /// This method calculates the required size using [EncodeSize::encode_size], allocates a
265    /// buffer of that exact capacity, writes the value using [Write::write], and performs a
266    /// sanity check assertion.
267    ///
268    /// # Panics
269    ///
270    /// Panics if `encode_size()` does not return the same number of bytes actually written by
271    /// `write()`
272    fn encode(&self) -> Bytes {
273        self.encode_mut().freeze()
274    }
275
276    /// Encodes `self` into a new [BytesMut] buffer.
277    ///
278    /// This method calculates the required size using [EncodeSize::encode_size], allocates a
279    /// buffer of that exact capacity, writes the value using [Write::write], and performs a
280    /// sanity check assertion.
281    ///
282    /// # Panics
283    ///
284    /// Panics if `encode_size()` does not return the same number of bytes actually written by
285    /// `write()`
286    fn encode_mut(&self) -> BytesMut {
287        let len = self.encode_size();
288        let mut buffer = BytesMut::with_capacity(len);
289        self.write(&mut buffer);
290        assert_eq!(buffer.len(), len, "write() did not write expected bytes");
291        buffer
292    }
293}
294
295// Automatically implement `Encode` for types that implement `Write` and `EncodeSize`.
296impl<T: Write + EncodeSize> Encode for T {}
297
298/// Convenience trait combining `Encode` with thread-safety bounds.
299///
300/// Represents types that can be fully encoded and safely shared across threads.
301pub trait EncodeShared: Encode + Send + Sync {}
302
303// Automatically implement `EncodeShared` for types that meet all bounds.
304impl<T: Encode + Send + Sync> EncodeShared for T {}
305
306/// Trait combining [Read] with a check for remaining bytes.
307///
308/// Ensures that *all* bytes from the input buffer were consumed during decoding.
309pub trait Decode: Read {
310    /// Decodes a value from `buf` using `cfg`, ensuring the entire buffer is consumed.
311    ///
312    /// Returns [Error] if decoding fails via [Read::read_cfg] or if there are leftover bytes in
313    /// `buf` after reading.
314    fn decode_cfg(mut buf: impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
315        let result = Self::read_cfg(&mut buf, cfg)?;
316
317        // Check that the buffer is fully consumed.
318        let remaining = buf.remaining();
319        if remaining > 0 {
320            return Err(Error::ExtraData(remaining));
321        }
322
323        Ok(result)
324    }
325}
326
327// Automatically implement `Decode` for types that implement `Read`.
328impl<T: Read> Decode for T {}
329
330/// Convenience trait combining [Encode] and [Decode].
331///
332/// Represents types that can be both fully encoded and decoded.
333pub trait Codec: Encode + Decode {}
334
335/// Automatically implement `Codec` for types that implement `Encode` and `Decode`.
336impl<T: Encode + Decode> Codec for T {}
337
338/// Convenience trait for [FixedSize] types that can be encoded directly into a fixed-size array.
339pub trait EncodeFixed: Write + FixedSize {
340    /// Encodes `self` into a fixed-size byte array `[u8; N]`.
341    ///
342    /// # Panics
343    ///
344    /// Panics if `N` is not equal to `<Self as FixedSize>::SIZE`.
345    /// Also panics if the `write()` implementation does not write exactly `N` bytes.
346    fn encode_fixed<const N: usize>(&self) -> [u8; N] {
347        // Ideally this is a compile-time check, but we can't do that in the current Rust version
348        // without adding a new generic parameter to the trait.
349        assert_eq!(
350            N,
351            Self::SIZE,
352            "Can't encode {} bytes into {} bytes",
353            Self::SIZE,
354            N
355        );
356
357        let mut array = [0u8; N];
358        let mut buf = &mut array[..];
359        self.write(&mut buf);
360        assert_eq!(buf.len(), 0);
361        array
362    }
363}
364
365// Automatically implement `EncodeFixed` for types that implement `Write` and `FixedSize`.
366impl<T: Write + FixedSize> EncodeFixed for T {}
367
368/// Convenience trait for [FixedSize] types that can be decoded directly from a fixed-size array.
369pub trait DecodeFixed: Read<Cfg = ()> + FixedSize {
370    /// Decodes a value from a fixed-size byte array `[u8; N]`, ensuring all bytes are consumed.
371    ///
372    /// # Panics
373    ///
374    /// Panics if `N` is not equal to `<Self as FixedSize>::SIZE`.
375    fn decode_fixed<const N: usize>(bytes: [u8; N]) -> Result<Self, Error> {
376        assert_eq!(
377            N,
378            Self::SIZE,
379            "Can't decode {} bytes into {} bytes",
380            N,
381            Self::SIZE
382        );
383
384        Self::decode_cfg(bytes.as_ref(), &())
385    }
386}
387
388// Automatically implement `DecodeFixed` for types that implement `Read<Cfg = ()>` and `FixedSize`.
389impl<T: Read<Cfg = ()> + FixedSize> DecodeFixed for T {}
390
391/// Convenience trait combining `FixedSize` and `Codec`.
392///
393/// Represents types that can be both fully encoded and decoded from a fixed-size byte sequence.
394pub trait CodecFixed: Codec + FixedSize {}
395
396// Automatically implement `CodecFixed` for types that implement `Codec` and `FixedSize`.
397impl<T: Codec + FixedSize> CodecFixed for T {}
398
399/// Convenience trait combining `Codec` with thread-safety bounds.
400///
401/// Represents types that can be fully encoded/decoded and safely shared across threads.
402pub trait CodecShared: Codec + Send + Sync {}
403
404// Automatically implement `CodecShared` for types that meet all bounds.
405impl<T: Codec + Send + Sync> CodecShared for T {}
406
407/// Convenience trait combining `CodecFixed` with thread-safety bounds and unit config.
408///
409/// Represents fixed-size types that can be fully encoded/decoded, require no configuration,
410/// and can be safely shared across threads.
411pub trait CodecFixedShared: CodecFixed<Cfg = ()> + Send + Sync {}
412
413// Automatically implement `CodecFixedShared` for types that meet all bounds.
414impl<T: CodecFixed<Cfg = ()> + Send + Sync> CodecFixedShared for T {}
415
416/// A [`BufMut`] that can also append pre-existing [`Bytes`] chunks.
417pub trait BufsMut: BufMut {
418    /// Appends a [`Bytes`] chunk instead of writing its contents inline into
419    /// the destination buffer.
420    fn push(&mut self, bytes: impl Into<Bytes>);
421}
422
423#[cfg(test)]
424mod tests {
425    use super::*;
426    use crate::{
427        Error, FixedArray,
428        extensions::{DecodeExt, ReadExt},
429    };
430    use bytes::Bytes;
431    use core::marker::PhantomData;
432
433    #[test]
434    fn test_insufficient_buffer() {
435        let mut reader = Bytes::from_static(&[0x01, 0x02]);
436        assert!(matches!(u32::read(&mut reader), Err(Error::EndOfBuffer)));
437    }
438
439    #[test]
440    fn test_extra_data() {
441        let encoded = Bytes::from_static(&[0x01, 0x02]);
442        assert!(matches!(u8::decode(encoded), Err(Error::ExtraData(1))));
443    }
444
445    #[test]
446    fn test_encode_fixed() {
447        let value = 42u32;
448        let encoded: [u8; 4] = value.encode_fixed();
449        let decoded = <u32>::decode(&encoded[..]).unwrap();
450        assert_eq!(value, decoded);
451    }
452
453    #[test]
454    fn test_arc_encode() {
455        let value = Arc::new(vec![1u8, 2, 3]);
456
457        assert_eq!(value.encode(), value.as_ref().encode());
458        assert_eq!(value.encode_size(), value.as_ref().encode_size());
459    }
460
461    #[test]
462    #[should_panic(expected = "Can't encode 4 bytes into 5 bytes")]
463    fn test_encode_fixed_panic() {
464        let _: [u8; 5] = 42u32.encode_fixed();
465    }
466
467    #[derive(Debug, Eq, PartialEq, FixedArray)]
468    struct FixedBytes([u8; 2]);
469
470    impl Write for FixedBytes {
471        fn write(&self, buf: &mut impl BufMut) {
472            self.0.write(buf);
473        }
474    }
475
476    impl Read for FixedBytes {
477        type Cfg = ();
478
479        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
480            Ok(Self(<[u8; Self::SIZE]>::read(buf)?))
481        }
482    }
483
484    impl FixedSize for FixedBytes {
485        const SIZE: usize = 2;
486    }
487
488    #[test]
489    fn test_fixed_array() {
490        let value = FixedBytes([1, 2]);
491        let encoded: [u8; FixedBytes::SIZE] = (&value).into();
492        assert_eq!(encoded, [1, 2]);
493        assert_eq!(<[u8; FixedBytes::SIZE]>::from(value), encoded);
494        assert_eq!(FixedBytes::try_from(encoded).unwrap(), FixedBytes([1, 2]));
495        assert_eq!(FixedBytes::try_from(&encoded).unwrap(), FixedBytes([1, 2]));
496        assert_eq!(
497            FixedBytes::try_from([1u8, 2].as_slice()).unwrap(),
498            FixedBytes([1, 2])
499        );
500        assert!(matches!(
501            FixedBytes::try_from([1u8].as_slice()),
502            Err(Error::EndOfBuffer)
503        ));
504        assert!(matches!(
505            FixedBytes::try_from([1u8, 2, 3].as_slice()),
506            Err(Error::ExtraData(1))
507        ));
508    }
509
510    #[test]
511    fn test_decode_fixed() {
512        assert_eq!(
513            FixedBytes::decode_fixed([1, 2]).unwrap(),
514            FixedBytes([1, 2])
515        );
516    }
517
518    #[test]
519    #[should_panic(expected = "Can't decode 3 bytes into 2 bytes")]
520    fn test_decode_fixed_panic() {
521        let _ = FixedBytes::decode_fixed([1, 2, 3]);
522    }
523
524    #[derive(Debug, Eq, PartialEq, FixedArray)]
525    #[fixed_array(infallible)]
526    struct InfallibleFixedBytes([u8; 2]);
527
528    impl Write for InfallibleFixedBytes {
529        fn write(&self, buf: &mut impl BufMut) {
530            self.0.write(buf);
531        }
532    }
533
534    impl Read for InfallibleFixedBytes {
535        type Cfg = ();
536
537        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
538            Ok(Self(<[u8; Self::SIZE]>::read(buf)?))
539        }
540    }
541
542    impl FixedSize for InfallibleFixedBytes {
543        const SIZE: usize = 2;
544    }
545
546    #[test]
547    fn test_fixed_array_infallible() {
548        let value = InfallibleFixedBytes([1, 2]);
549        let encoded: [u8; InfallibleFixedBytes::SIZE] = (&value).into();
550        assert_eq!(encoded, [1, 2]);
551        assert_eq!(<[u8; InfallibleFixedBytes::SIZE]>::from(value), encoded);
552        assert_eq!(
553            InfallibleFixedBytes::from(encoded),
554            InfallibleFixedBytes([1, 2])
555        );
556        assert_eq!(
557            InfallibleFixedBytes::from(&encoded),
558            InfallibleFixedBytes([1, 2])
559        );
560        assert_eq!(
561            InfallibleFixedBytes::try_from([1u8, 2].as_slice()).unwrap(),
562            InfallibleFixedBytes([1, 2])
563        );
564        assert!(matches!(
565            InfallibleFixedBytes::try_from([1u8, 2, 3].as_slice()),
566            Err(Error::ExtraData(1))
567        ));
568    }
569
570    #[derive(Debug, Eq, PartialEq, FixedArray)]
571    #[fixed_array(bytes([u8; N]))]
572    struct GenericFixed<const N: usize>([u8; N]);
573
574    impl<const N: usize> Write for GenericFixed<N> {
575        fn write(&self, buf: &mut impl BufMut) {
576            self.0.write(buf);
577        }
578    }
579
580    impl<const N: usize> Read for GenericFixed<N> {
581        type Cfg = ();
582
583        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
584            Ok(Self(<[u8; N]>::read(buf)?))
585        }
586    }
587
588    impl<const N: usize> FixedSize for GenericFixed<N> {
589        const SIZE: usize = N;
590    }
591
592    #[test]
593    fn test_fixed_array_generic() {
594        let value = GenericFixed::<3>([1, 2, 3]);
595        let encoded: [u8; 3] = (&value).into();
596        assert_eq!(encoded, [1, 2, 3]);
597        assert_eq!(<[u8; 3]>::from(value), encoded);
598        assert_eq!(
599            GenericFixed::<3>::try_from(encoded).unwrap(),
600            GenericFixed([1, 2, 3])
601        );
602        assert_eq!(
603            GenericFixed::<3>::try_from(&encoded).unwrap(),
604            GenericFixed([1, 2, 3])
605        );
606        assert_eq!(
607            GenericFixed::<3>::try_from([1u8, 2, 3].as_slice()).unwrap(),
608            GenericFixed([1, 2, 3])
609        );
610    }
611
612    #[derive(Debug, Eq, PartialEq, FixedArray)]
613    #[fixed_array(infallible, bytes([u8; N]))]
614    struct GenericInfallible<const N: usize>([u8; N]);
615
616    impl<const N: usize> Write for GenericInfallible<N> {
617        fn write(&self, buf: &mut impl BufMut) {
618            self.0.write(buf);
619        }
620    }
621
622    impl<const N: usize> Read for GenericInfallible<N> {
623        type Cfg = ();
624
625        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
626            Ok(Self(<[u8; N]>::read(buf)?))
627        }
628    }
629
630    impl<const N: usize> FixedSize for GenericInfallible<N> {
631        const SIZE: usize = N;
632    }
633
634    #[test]
635    fn test_fixed_array_generic_infallible() {
636        let value = GenericInfallible::<3>([1, 2, 3]);
637        let encoded: [u8; 3] = (&value).into();
638        assert_eq!(encoded, [1, 2, 3]);
639        assert_eq!(<[u8; 3]>::from(value), encoded);
640        assert_eq!(
641            GenericInfallible::<3>::from(encoded),
642            GenericInfallible([1, 2, 3])
643        );
644        assert_eq!(
645            GenericInfallible::<3>::from(&encoded),
646            GenericInfallible([1, 2, 3])
647        );
648        assert_eq!(
649            GenericInfallible::<3>::try_from([1u8, 2, 3].as_slice()).unwrap(),
650            GenericInfallible([1, 2, 3])
651        );
652    }
653
654    trait FixedArrayBound {}
655
656    #[derive(Debug, Eq, PartialEq)]
657    struct Bounded;
658
659    impl FixedArrayBound for Bounded {}
660
661    #[derive(Debug, Eq, PartialEq, FixedArray)]
662    #[fixed_array(bytes([u8; 2]))]
663    struct BoundedGeneric<T> {
664        marker: PhantomData<T>,
665        raw: [u8; 2],
666    }
667
668    impl<T: FixedArrayBound> Write for BoundedGeneric<T> {
669        fn write(&self, buf: &mut impl BufMut) {
670            self.raw.write(buf);
671        }
672    }
673
674    impl<T: FixedArrayBound> Read for BoundedGeneric<T> {
675        type Cfg = ();
676
677        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
678            Ok(Self {
679                marker: PhantomData,
680                raw: <[u8; 2]>::read(buf)?,
681            })
682        }
683    }
684
685    impl<T: FixedArrayBound> FixedSize for BoundedGeneric<T> {
686        const SIZE: usize = 2;
687    }
688
689    #[test]
690    fn test_fixed_array_bounded_generic() {
691        let value = BoundedGeneric::<Bounded> {
692            marker: PhantomData,
693            raw: [1, 2],
694        };
695        let encoded: [u8; 2] = (&value).into();
696        assert_eq!(encoded, [1, 2]);
697        assert_eq!(<[u8; 2]>::from(value).as_ref(), &[1, 2]);
698        assert_eq!(
699            BoundedGeneric::<Bounded>::try_from(encoded).unwrap().raw,
700            [1, 2]
701        );
702        assert_eq!(
703            BoundedGeneric::<Bounded>::try_from(&encoded).unwrap().raw,
704            [1, 2]
705        );
706        assert_eq!(
707            BoundedGeneric::<Bounded>::try_from([1u8, 2].as_slice())
708                .unwrap()
709                .raw,
710            [1, 2]
711        );
712    }
713
714    #[derive(Debug, Eq, PartialEq, FixedArray)]
715    #[fixed_array(bytes([u8; 2]))]
716    struct LifetimeFixed<'a> {
717        marker: PhantomData<&'a ()>,
718        raw: [u8; 2],
719    }
720
721    impl Write for LifetimeFixed<'_> {
722        fn write(&self, buf: &mut impl BufMut) {
723            self.raw.write(buf);
724        }
725    }
726
727    impl Read for LifetimeFixed<'_> {
728        type Cfg = ();
729
730        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
731            Ok(Self {
732                marker: PhantomData,
733                raw: <[u8; 2]>::read(buf)?,
734            })
735        }
736    }
737
738    impl FixedSize for LifetimeFixed<'_> {
739        const SIZE: usize = 2;
740    }
741
742    #[test]
743    fn test_fixed_array_lifetime() {
744        let value = LifetimeFixed {
745            marker: PhantomData,
746            raw: [1, 2],
747        };
748        let encoded: [u8; LifetimeFixed::SIZE] = (&value).into();
749        assert_eq!(encoded, [1, 2]);
750        assert_eq!(<[u8; LifetimeFixed::SIZE]>::from(value).as_ref(), &[1, 2]);
751        assert_eq!(LifetimeFixed::try_from(encoded).unwrap().raw, [1, 2]);
752        assert_eq!(LifetimeFixed::try_from(&encoded).unwrap().raw, [1, 2]);
753        assert_eq!(
754            LifetimeFixed::try_from([1u8, 2].as_slice()).unwrap().raw,
755            [1, 2]
756        );
757    }
758}