Skip to main content

commonware_codec/
codec.rs

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