wire_repr/codec/fixed.rs
1//! Fixed-width codec contracts.
2
3use super::EncodePlan;
4
5/// A codec whose encoded representation always has one fixed width.
6///
7/// [`Self::WIDTH`] must be nonzero. Every successful [`Self::plan`] must report that
8/// exact encoded length. For every such plan, [`EncodePlan::write_into`] called with
9/// an output slice of exactly [`Self::WIDTH`] bytes must write a complete representation
10/// whose decoding recovers the same semantic value supplied to [`Self::plan`]. Decoding
11/// is total for every exact-width byte pattern. A codec that violates these requirements
12/// is contract-invalid.
13///
14/// When a layout builder derives a region length through
15/// `Self::Value<'static>: TryFrom<usize>`, the complete conversion and codec round trip
16/// must preserve that length: converting the decoded planned representation back to
17/// `usize` must produce the original region length.
18///
19/// [`Self::plan`] completes all fallible encoding work before a caller mutates an output
20/// buffer. Layout parsing establishes exact-width bounds before calling [`Self::decode`].
21pub trait FixedCodec {
22 /// Semantic value represented by an exact-width wire representation.
23 type Value<'wire>
24 where
25 Self: 'wire;
26
27 /// Error returned while preparing an encoded value.
28 type EncodeError: core::fmt::Debug;
29
30 /// Prepared fixed-width encoded bytes.
31 type Plan<'value>: EncodePlan
32 where
33 Self: 'value;
34
35 /// Number of bytes in every encoded representation.
36 const WIDTH: usize;
37
38 /// Decodes an exact-width encoded representation.
39 fn decode<'wire>(bytes: &'wire [u8]) -> Self::Value<'wire>;
40
41 /// Prepares the complete encoded representation without mutating a caller buffer.
42 fn plan<'value>(value: Self::Value<'value>) -> Result<Self::Plan<'value>, Self::EncodeError>;
43}
44
45/// Error returned when an exact-width byte value has the wrong length.
46#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47pub struct ExactWidthError {
48 expected: usize,
49 actual: usize,
50}
51
52impl ExactWidthError {
53 /// Creates an error for an exact-width mismatch.
54 #[must_use]
55 pub const fn new(expected: usize, actual: usize) -> Self {
56 Self { expected, actual }
57 }
58
59 /// Returns the required byte length.
60 #[must_use]
61 pub const fn expected(&self) -> usize {
62 self.expected
63 }
64
65 /// Returns the supplied byte length.
66 #[must_use]
67 pub const fn actual(&self) -> usize {
68 self.actual
69 }
70}
71
72impl core::fmt::Display for ExactWidthError {
73 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74 write!(
75 formatter,
76 "fixed codec expected {} bytes, got {}",
77 self.expected, self.actual
78 )
79 }
80}
81
82impl core::error::Error for ExactWidthError {}
83
84/// A borrowed fixed-width span of wire bytes with no content interpretation.
85///
86/// `N` must be nonzero. Using `Bytes<0>` as a [`FixedCodec`] fails during constant
87/// evaluation rather than exposing a codec that violates [`FixedCodec::WIDTH`].
88///
89/// ```compile_fail
90/// use wire_repr::{Bytes, FixedCodec};
91///
92/// let _ = <Bytes<0> as FixedCodec>::WIDTH;
93/// ```
94///
95/// `Bytes<N>` decodes to the exact borrowed wire slice and plans an equally borrowed input
96/// slice for copying at write time. It does not validate magic values, reserved bytes, or
97/// any other domain semantics; consumers own those policies.
98#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
99pub struct Bytes<const N: usize>;
100
101impl<const N: usize> FixedCodec for Bytes<N> {
102 type Value<'wire>
103 = &'wire [u8]
104 where
105 Self: 'wire;
106 type EncodeError = ExactWidthError;
107 type Plan<'value>
108 = &'value [u8]
109 where
110 Self: 'value;
111
112 const WIDTH: usize = {
113 assert!(N != 0, "Bytes<N> requires a nonzero width");
114 N
115 };
116
117 #[inline]
118 fn decode<'wire>(bytes: &'wire [u8]) -> Self::Value<'wire> {
119 const { assert!(N != 0, "Bytes<N> requires a nonzero width") };
120 bytes
121 }
122
123 #[inline]
124 fn plan<'value>(value: Self::Value<'value>) -> Result<Self::Plan<'value>, Self::EncodeError> {
125 const { assert!(N != 0, "Bytes<N> requires a nonzero width") };
126 if value.len() == N {
127 Ok(value)
128 } else {
129 Err(ExactWidthError::new(N, value.len()))
130 }
131 }
132}