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    /// Returns [Error] if decoding fails due to invalid data, insufficient bytes in the buffer,
211    /// or violation of constraints imposed by the `cfg`.
212    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error>;
213
214    /// Reads `len` values from the buffer into a vector.
215    ///
216    /// Container implementations call this hook so element types can provide a more efficient
217    /// vector read path. The default preserves normal element-by-element decoding.
218    ///
219    /// This hook exists because stable Rust cannot express the overlapping specialized
220    /// container impls that would otherwise provide this aggregate path directly.
221    ///
222    /// This is hidden from generated documentation because it is an implementation hook for
223    /// codec's container types, not part of the intended user-facing API. Most users should
224    /// implement [Read::read_cfg] instead.
225    #[doc(hidden)]
226    #[inline]
227    fn read_vec(buf: &mut impl Buf, len: usize, cfg: &Self::Cfg) -> Result<Vec<Self>, Error> {
228        let mut values = Vec::with_capacity(len);
229        for _ in 0..len {
230            values.push(Self::read_cfg(buf, cfg)?);
231        }
232        Ok(values)
233    }
234
235    /// Reads exactly `N` values from the buffer into an array.
236    ///
237    /// This hidden hook is the array equivalent of [Read::read_vec]. The default preserves
238    /// normal element-by-element decoding.
239    ///
240    /// This hook exists because stable Rust cannot express the overlapping specialized array
241    /// impls that would otherwise provide this aggregate path directly.
242    ///
243    /// This is hidden from generated documentation for the same reason as [Read::read_vec].
244    #[doc(hidden)]
245    #[inline]
246    fn read_array<const N: usize>(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<[Self; N], Error> {
247        Ok(Self::read_vec(buf, N, cfg)?
248            .try_into()
249            .unwrap_or_else(|_| unreachable!("array length should match capacity")))
250    }
251}
252
253/// Trait combining [Write] and [EncodeSize] for types that can be fully encoded.
254///
255/// This trait provides the convenience [Encode::encode] method which handles
256/// buffer allocation, writing, and size assertion in one go.
257pub trait Encode: Write + EncodeSize {
258    /// Encodes `self` into a new [Bytes] buffer.
259    ///
260    /// This method calculates the required size using [EncodeSize::encode_size], allocates a
261    /// buffer of that exact capacity, writes the value using [Write::write], and performs a
262    /// sanity check assertion.
263    ///
264    /// # Panics
265    ///
266    /// Panics if `encode_size()` does not return the same number of bytes actually written by
267    /// `write()`
268    fn encode(&self) -> Bytes {
269        self.encode_mut().freeze()
270    }
271
272    /// Encodes `self` into a new [BytesMut] buffer.
273    ///
274    /// This method calculates the required size using [EncodeSize::encode_size], allocates a
275    /// buffer of that exact capacity, writes the value using [Write::write], and performs a
276    /// sanity check assertion.
277    ///
278    /// # Panics
279    ///
280    /// Panics if `encode_size()` does not return the same number of bytes actually written by
281    /// `write()`
282    fn encode_mut(&self) -> BytesMut {
283        let len = self.encode_size();
284        let mut buffer = BytesMut::with_capacity(len);
285        self.write(&mut buffer);
286        assert_eq!(buffer.len(), len, "write() did not write expected bytes");
287        buffer
288    }
289}
290
291// Automatically implement `Encode` for types that implement `Write` and `EncodeSize`.
292impl<T: Write + EncodeSize> Encode for T {}
293
294/// Convenience trait combining `Encode` with thread-safety bounds.
295///
296/// Represents types that can be fully encoded and safely shared across threads.
297pub trait EncodeShared: Encode + Send + Sync {}
298
299// Automatically implement `EncodeShared` for types that meet all bounds.
300impl<T: Encode + Send + Sync> EncodeShared for T {}
301
302/// Trait combining [Read] with a check for remaining bytes.
303///
304/// Ensures that *all* bytes from the input buffer were consumed during decoding.
305pub trait Decode: Read {
306    /// Decodes a value from `buf` using `cfg`, ensuring the entire buffer is consumed.
307    ///
308    /// Returns [Error] if decoding fails via [Read::read_cfg] or if there are leftover bytes in
309    /// `buf` after reading.
310    fn decode_cfg(mut buf: impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
311        let result = Self::read_cfg(&mut buf, cfg)?;
312
313        // Check that the buffer is fully consumed.
314        let remaining = buf.remaining();
315        if remaining > 0 {
316            return Err(Error::ExtraData(remaining));
317        }
318
319        Ok(result)
320    }
321}
322
323// Automatically implement `Decode` for types that implement `Read`.
324impl<T: Read> Decode for T {}
325
326/// Convenience trait combining [Encode] and [Decode].
327///
328/// Represents types that can be both fully encoded and decoded.
329pub trait Codec: Encode + Decode {}
330
331/// Automatically implement `Codec` for types that implement `Encode` and `Decode`.
332impl<T: Encode + Decode> Codec for T {}
333
334/// Convenience trait for [FixedSize] types that can be encoded directly into a fixed-size array.
335pub trait EncodeFixed: Write + FixedSize {
336    /// Encodes `self` into a fixed-size byte array `[u8; N]`.
337    ///
338    /// # Panics
339    ///
340    /// Panics if `N` is not equal to `<Self as FixedSize>::SIZE`.
341    /// Also panics if the `write()` implementation does not write exactly `N` bytes.
342    fn encode_fixed<const N: usize>(&self) -> [u8; N] {
343        // Ideally this is a compile-time check, but we can't do that in the current Rust version
344        // without adding a new generic parameter to the trait.
345        assert_eq!(
346            N,
347            Self::SIZE,
348            "Can't encode {} bytes into {} bytes",
349            Self::SIZE,
350            N
351        );
352
353        let mut array = [0u8; N];
354        let mut buf = &mut array[..];
355        self.write(&mut buf);
356        assert_eq!(buf.len(), 0);
357        array
358    }
359}
360
361// Automatically implement `EncodeFixed` for types that implement `Write` and `FixedSize`.
362impl<T: Write + FixedSize> EncodeFixed for T {}
363
364/// Convenience trait for [FixedSize] types that can be decoded directly from a fixed-size array.
365pub trait DecodeFixed: Read<Cfg = ()> + FixedSize {
366    /// Decodes a value from a fixed-size byte array `[u8; N]`, ensuring all bytes are consumed.
367    ///
368    /// # Panics
369    ///
370    /// Panics if `N` is not equal to `<Self as FixedSize>::SIZE`.
371    fn decode_fixed<const N: usize>(bytes: [u8; N]) -> Result<Self, Error> {
372        assert_eq!(
373            N,
374            Self::SIZE,
375            "Can't decode {} bytes into {} bytes",
376            N,
377            Self::SIZE
378        );
379
380        Self::decode_cfg(bytes.as_ref(), &())
381    }
382}
383
384// Automatically implement `DecodeFixed` for types that implement `Read<Cfg = ()>` and `FixedSize`.
385impl<T: Read<Cfg = ()> + FixedSize> DecodeFixed for T {}
386
387/// Convenience trait combining `FixedSize` and `Codec`.
388///
389/// Represents types that can be both fully encoded and decoded from a fixed-size byte sequence.
390pub trait CodecFixed: Codec + FixedSize {}
391
392// Automatically implement `CodecFixed` for types that implement `Codec` and `FixedSize`.
393impl<T: Codec + FixedSize> CodecFixed for T {}
394
395/// Convenience trait combining `Codec` with thread-safety bounds.
396///
397/// Represents types that can be fully encoded/decoded and safely shared across threads.
398pub trait CodecShared: Codec + Send + Sync {}
399
400// Automatically implement `CodecShared` for types that meet all bounds.
401impl<T: Codec + Send + Sync> CodecShared for T {}
402
403/// Convenience trait combining `CodecFixed` with thread-safety bounds and unit config.
404///
405/// Represents fixed-size types that can be fully encoded/decoded, require no configuration,
406/// and can be safely shared across threads.
407pub trait CodecFixedShared: CodecFixed<Cfg = ()> + Send + Sync {}
408
409// Automatically implement `CodecFixedShared` for types that meet all bounds.
410impl<T: CodecFixed<Cfg = ()> + Send + Sync> CodecFixedShared for T {}
411
412/// A [`BufMut`] that can also append pre-existing [`Bytes`] chunks.
413pub trait BufsMut: BufMut {
414    /// Appends a [`Bytes`] chunk instead of writing its contents inline into
415    /// the destination buffer.
416    fn push(&mut self, bytes: impl Into<Bytes>);
417}
418
419#[cfg(test)]
420mod tests {
421    use super::*;
422    use crate::{
423        extensions::{DecodeExt, ReadExt},
424        Error, FixedArray,
425    };
426    use bytes::Bytes;
427    use core::marker::PhantomData;
428
429    #[test]
430    fn test_insufficient_buffer() {
431        let mut reader = Bytes::from_static(&[0x01, 0x02]);
432        assert!(matches!(u32::read(&mut reader), Err(Error::EndOfBuffer)));
433    }
434
435    #[test]
436    fn test_extra_data() {
437        let encoded = Bytes::from_static(&[0x01, 0x02]);
438        assert!(matches!(u8::decode(encoded), Err(Error::ExtraData(1))));
439    }
440
441    #[test]
442    fn test_encode_fixed() {
443        let value = 42u32;
444        let encoded: [u8; 4] = value.encode_fixed();
445        let decoded = <u32>::decode(&encoded[..]).unwrap();
446        assert_eq!(value, decoded);
447    }
448
449    #[test]
450    fn test_arc_encode() {
451        let value = Arc::new(vec![1u8, 2, 3]);
452
453        assert_eq!(value.encode(), value.as_ref().encode());
454        assert_eq!(value.encode_size(), value.as_ref().encode_size());
455    }
456
457    #[test]
458    #[should_panic(expected = "Can't encode 4 bytes into 5 bytes")]
459    fn test_encode_fixed_panic() {
460        let _: [u8; 5] = 42u32.encode_fixed();
461    }
462
463    #[derive(Debug, Eq, PartialEq, FixedArray)]
464    struct FixedBytes([u8; 2]);
465
466    impl Write for FixedBytes {
467        fn write(&self, buf: &mut impl BufMut) {
468            self.0.write(buf);
469        }
470    }
471
472    impl Read for FixedBytes {
473        type Cfg = ();
474
475        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
476            Ok(Self(<[u8; Self::SIZE]>::read(buf)?))
477        }
478    }
479
480    impl FixedSize for FixedBytes {
481        const SIZE: usize = 2;
482    }
483
484    #[test]
485    fn test_fixed_array() {
486        let value = FixedBytes([1, 2]);
487        let encoded: [u8; FixedBytes::SIZE] = (&value).into();
488        assert_eq!(encoded, [1, 2]);
489        assert_eq!(<[u8; FixedBytes::SIZE]>::from(value), encoded);
490        assert_eq!(FixedBytes::try_from(encoded).unwrap(), FixedBytes([1, 2]));
491        assert_eq!(FixedBytes::try_from(&encoded).unwrap(), FixedBytes([1, 2]));
492        assert_eq!(
493            FixedBytes::try_from([1u8, 2].as_slice()).unwrap(),
494            FixedBytes([1, 2])
495        );
496        assert!(matches!(
497            FixedBytes::try_from([1u8].as_slice()),
498            Err(Error::EndOfBuffer)
499        ));
500        assert!(matches!(
501            FixedBytes::try_from([1u8, 2, 3].as_slice()),
502            Err(Error::ExtraData(1))
503        ));
504    }
505
506    #[test]
507    fn test_decode_fixed() {
508        assert_eq!(
509            FixedBytes::decode_fixed([1, 2]).unwrap(),
510            FixedBytes([1, 2])
511        );
512    }
513
514    #[test]
515    #[should_panic(expected = "Can't decode 3 bytes into 2 bytes")]
516    fn test_decode_fixed_panic() {
517        let _ = FixedBytes::decode_fixed([1, 2, 3]);
518    }
519
520    #[derive(Debug, Eq, PartialEq, FixedArray)]
521    #[fixed_array(infallible)]
522    struct InfallibleFixedBytes([u8; 2]);
523
524    impl Write for InfallibleFixedBytes {
525        fn write(&self, buf: &mut impl BufMut) {
526            self.0.write(buf);
527        }
528    }
529
530    impl Read for InfallibleFixedBytes {
531        type Cfg = ();
532
533        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
534            Ok(Self(<[u8; Self::SIZE]>::read(buf)?))
535        }
536    }
537
538    impl FixedSize for InfallibleFixedBytes {
539        const SIZE: usize = 2;
540    }
541
542    #[test]
543    fn test_fixed_array_infallible() {
544        let value = InfallibleFixedBytes([1, 2]);
545        let encoded: [u8; InfallibleFixedBytes::SIZE] = (&value).into();
546        assert_eq!(encoded, [1, 2]);
547        assert_eq!(<[u8; InfallibleFixedBytes::SIZE]>::from(value), encoded);
548        assert_eq!(
549            InfallibleFixedBytes::from(encoded),
550            InfallibleFixedBytes([1, 2])
551        );
552        assert_eq!(
553            InfallibleFixedBytes::from(&encoded),
554            InfallibleFixedBytes([1, 2])
555        );
556        assert_eq!(
557            InfallibleFixedBytes::try_from([1u8, 2].as_slice()).unwrap(),
558            InfallibleFixedBytes([1, 2])
559        );
560        assert!(matches!(
561            InfallibleFixedBytes::try_from([1u8, 2, 3].as_slice()),
562            Err(Error::ExtraData(1))
563        ));
564    }
565
566    #[derive(Debug, Eq, PartialEq, FixedArray)]
567    #[fixed_array(bytes([u8; N]))]
568    struct GenericFixed<const N: usize>([u8; N]);
569
570    impl<const N: usize> Write for GenericFixed<N> {
571        fn write(&self, buf: &mut impl BufMut) {
572            self.0.write(buf);
573        }
574    }
575
576    impl<const N: usize> Read for GenericFixed<N> {
577        type Cfg = ();
578
579        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
580            Ok(Self(<[u8; N]>::read(buf)?))
581        }
582    }
583
584    impl<const N: usize> FixedSize for GenericFixed<N> {
585        const SIZE: usize = N;
586    }
587
588    #[test]
589    fn test_fixed_array_generic() {
590        let value = GenericFixed::<3>([1, 2, 3]);
591        let encoded: [u8; 3] = (&value).into();
592        assert_eq!(encoded, [1, 2, 3]);
593        assert_eq!(<[u8; 3]>::from(value), encoded);
594        assert_eq!(
595            GenericFixed::<3>::try_from(encoded).unwrap(),
596            GenericFixed([1, 2, 3])
597        );
598        assert_eq!(
599            GenericFixed::<3>::try_from(&encoded).unwrap(),
600            GenericFixed([1, 2, 3])
601        );
602        assert_eq!(
603            GenericFixed::<3>::try_from([1u8, 2, 3].as_slice()).unwrap(),
604            GenericFixed([1, 2, 3])
605        );
606    }
607
608    #[derive(Debug, Eq, PartialEq, FixedArray)]
609    #[fixed_array(infallible, bytes([u8; N]))]
610    struct GenericInfallible<const N: usize>([u8; N]);
611
612    impl<const N: usize> Write for GenericInfallible<N> {
613        fn write(&self, buf: &mut impl BufMut) {
614            self.0.write(buf);
615        }
616    }
617
618    impl<const N: usize> Read for GenericInfallible<N> {
619        type Cfg = ();
620
621        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
622            Ok(Self(<[u8; N]>::read(buf)?))
623        }
624    }
625
626    impl<const N: usize> FixedSize for GenericInfallible<N> {
627        const SIZE: usize = N;
628    }
629
630    #[test]
631    fn test_fixed_array_generic_infallible() {
632        let value = GenericInfallible::<3>([1, 2, 3]);
633        let encoded: [u8; 3] = (&value).into();
634        assert_eq!(encoded, [1, 2, 3]);
635        assert_eq!(<[u8; 3]>::from(value), encoded);
636        assert_eq!(
637            GenericInfallible::<3>::from(encoded),
638            GenericInfallible([1, 2, 3])
639        );
640        assert_eq!(
641            GenericInfallible::<3>::from(&encoded),
642            GenericInfallible([1, 2, 3])
643        );
644        assert_eq!(
645            GenericInfallible::<3>::try_from([1u8, 2, 3].as_slice()).unwrap(),
646            GenericInfallible([1, 2, 3])
647        );
648    }
649
650    trait FixedArrayBound {}
651
652    #[derive(Debug, Eq, PartialEq)]
653    struct Bounded;
654
655    impl FixedArrayBound for Bounded {}
656
657    #[derive(Debug, Eq, PartialEq, FixedArray)]
658    #[fixed_array(bytes([u8; 2]))]
659    struct BoundedGeneric<T> {
660        marker: PhantomData<T>,
661        raw: [u8; 2],
662    }
663
664    impl<T: FixedArrayBound> Write for BoundedGeneric<T> {
665        fn write(&self, buf: &mut impl BufMut) {
666            self.raw.write(buf);
667        }
668    }
669
670    impl<T: FixedArrayBound> Read for BoundedGeneric<T> {
671        type Cfg = ();
672
673        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
674            Ok(Self {
675                marker: PhantomData,
676                raw: <[u8; 2]>::read(buf)?,
677            })
678        }
679    }
680
681    impl<T: FixedArrayBound> FixedSize for BoundedGeneric<T> {
682        const SIZE: usize = 2;
683    }
684
685    #[test]
686    fn test_fixed_array_bounded_generic() {
687        let value = BoundedGeneric::<Bounded> {
688            marker: PhantomData,
689            raw: [1, 2],
690        };
691        let encoded: [u8; 2] = (&value).into();
692        assert_eq!(encoded, [1, 2]);
693        assert_eq!(<[u8; 2]>::from(value).as_ref(), &[1, 2]);
694        assert_eq!(
695            BoundedGeneric::<Bounded>::try_from(encoded).unwrap().raw,
696            [1, 2]
697        );
698        assert_eq!(
699            BoundedGeneric::<Bounded>::try_from(&encoded).unwrap().raw,
700            [1, 2]
701        );
702        assert_eq!(
703            BoundedGeneric::<Bounded>::try_from([1u8, 2].as_slice())
704                .unwrap()
705                .raw,
706            [1, 2]
707        );
708    }
709
710    #[derive(Debug, Eq, PartialEq, FixedArray)]
711    #[fixed_array(bytes([u8; 2]))]
712    struct LifetimeFixed<'a> {
713        marker: PhantomData<&'a ()>,
714        raw: [u8; 2],
715    }
716
717    impl Write for LifetimeFixed<'_> {
718        fn write(&self, buf: &mut impl BufMut) {
719            self.raw.write(buf);
720        }
721    }
722
723    impl Read for LifetimeFixed<'_> {
724        type Cfg = ();
725
726        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
727            Ok(Self {
728                marker: PhantomData,
729                raw: <[u8; 2]>::read(buf)?,
730            })
731        }
732    }
733
734    impl FixedSize for LifetimeFixed<'_> {
735        const SIZE: usize = 2;
736    }
737
738    #[test]
739    fn test_fixed_array_lifetime() {
740        let value = LifetimeFixed {
741            marker: PhantomData,
742            raw: [1, 2],
743        };
744        let encoded: [u8; LifetimeFixed::SIZE] = (&value).into();
745        assert_eq!(encoded, [1, 2]);
746        assert_eq!(<[u8; LifetimeFixed::SIZE]>::from(value).as_ref(), &[1, 2]);
747        assert_eq!(LifetimeFixed::try_from(encoded).unwrap().raw, [1, 2]);
748        assert_eq!(LifetimeFixed::try_from(&encoded).unwrap().raw, [1, 2]);
749        assert_eq!(
750            LifetimeFixed::try_from([1u8, 2].as_slice()).unwrap().raw,
751            [1, 2]
752        );
753    }
754}