messagepack_core/extension/
mod.rs

1//! MessagePack extension helpers.
2
3mod decode;
4mod encode;
5
6use crate::decode::Error as DecodeError;
7use crate::{formats::Format, io::IoRead};
8
9const U8_MAX: usize = u8::MAX as usize;
10const U16_MAX: usize = u16::MAX as usize;
11const U32_MAX: usize = u32::MAX as usize;
12const U8_MAX_PLUS_ONE: usize = U8_MAX + 1;
13const U16_MAX_PLUS_ONE: usize = U16_MAX + 1;
14
15// Read extension header and return (length, type)
16pub(crate) fn read_ext_header<'de, R>(
17    format: Format,
18    reader: &mut R,
19) -> Result<(usize, i8), DecodeError<R::Error>>
20where
21    R: IoRead<'de>,
22{
23    use crate::decode::NbyteReader;
24    let len = match format {
25        Format::FixExt1 => 1,
26        Format::FixExt2 => 2,
27        Format::FixExt4 => 4,
28        Format::FixExt8 => 8,
29        Format::FixExt16 => 16,
30        Format::Ext8 => NbyteReader::<1>::read(reader)?,
31        Format::Ext16 => NbyteReader::<2>::read(reader)?,
32        Format::Ext32 => NbyteReader::<4>::read(reader)?,
33        _ => return Err(DecodeError::UnexpectedFormat),
34    };
35
36    let ext_type: [u8; 1] = reader
37        .read_slice(1)
38        .map_err(DecodeError::Io)?
39        .as_bytes()
40        .try_into()
41        .map_err(|_| DecodeError::UnexpectedEof)?;
42    let ty = ext_type[0] as i8;
43
44    Ok((len, ty))
45}
46
47/// A borrowed view of a MessagePack extension value.
48///
49/// Note that the MessagePack header (FixExt vs Ext8/16/32) is determined by the
50/// payload length when encoding. See [`ExtensionRef::to_format`].
51#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
52pub struct ExtensionRef<'a> {
53    /// Application‑defined extension type code.
54    pub r#type: i8,
55    /// Borrowed payload bytes.
56    pub data: &'a [u8],
57}
58
59impl<'a> ExtensionRef<'a> {
60    /// Create a borrowed reference to extension data with the given type code.
61    pub fn new(r#type: i8, data: &'a [u8]) -> Self {
62        Self { r#type, data }
63    }
64
65    /// Decide the MessagePack format to use given the payload length.
66    ///
67    /// - If `data.len()` is exactly 1, 2, 4, 8 or 16, `FixExtN` is selected.
68    /// - Otherwise, `Ext8`/`Ext16`/`Ext32` is selected based on the byte length.
69    pub fn to_format<E>(&self) -> core::result::Result<Format, crate::encode::Error<E>> {
70        let format = match self.data.len() {
71            1 => Format::FixExt1,
72            2 => Format::FixExt2,
73            4 => Format::FixExt4,
74            8 => Format::FixExt8,
75            16 => Format::FixExt16,
76            0..=U8_MAX => Format::Ext8,
77            U8_MAX_PLUS_ONE..=U16_MAX => Format::Ext16,
78            U16_MAX_PLUS_ONE..=U32_MAX => Format::Ext32,
79            _ => return Err(crate::encode::Error::InvalidFormat),
80        };
81        Ok(format)
82    }
83}
84
85/// A fixed-capacity container for extension payloads of up to `N` bytes.
86///
87/// This type name refers to the fixed-size backing buffer, not the MessagePack
88/// header kind. The actual header used at encode-time depends on the current
89/// payload length:
90/// - `len == 1, 2, 4, 8, 16` → `FixExtN`
91/// - otherwise (0..=255, 256..=65535, 65536..=u32::MAX) → `Ext8/16/32`
92#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
93pub struct FixedExtension<const N: usize> {
94    /// Application‑defined extension type code.
95    pub r#type: i8,
96    len: usize,
97    data: [u8; N],
98}
99
100/// Error indicating that extension payload exceeds the fixed capacity `N`.
101#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
102pub struct ExtensionCapacityError(());
103
104impl<const N: usize> FixedExtension<N> {
105    /// Construct from a slice whose length must be `<= N`.
106    ///
107    /// The chosen MessagePack format when encoding still follows the rules
108    /// described in the type-level documentation above.
109    pub fn new(r#type: i8, data: &[u8]) -> Option<Self> {
110        if data.len() > N {
111            return None;
112        }
113        let mut buf = [0u8; N];
114        buf[..data.len()].copy_from_slice(data);
115        Some(Self {
116            r#type,
117            len: data.len(),
118            data: buf,
119        })
120    }
121    /// Construct with an exact `N`-byte payload
122    pub fn new_fixed(r#type: i8, data: [u8; N]) -> Self {
123        Self {
124            r#type,
125            len: N,
126            data,
127        }
128    }
129
130    /// Construct with a logical prefix
131    pub fn new_fixed_with_prefix(
132        r#type: i8,
133        len: usize,
134        data: [u8; N],
135    ) -> core::result::Result<Self, ExtensionCapacityError> {
136        if len <= N {
137            Ok(Self { r#type, len, data })
138        } else {
139            Err(ExtensionCapacityError(()))
140        }
141    }
142
143    /// Borrow as [`ExtensionRef`] for encoding.
144    pub fn as_ref(&self) -> ExtensionRef<'_> {
145        ExtensionRef {
146            r#type: self.r#type,
147            data: &self.data[..self.len],
148        }
149    }
150
151    /// Current payload length in bytes.
152    pub fn len(&self) -> usize {
153        self.len
154    }
155
156    /// Returns `true` if the payload is empty.
157    pub fn is_empty(&self) -> bool {
158        self.len == 0
159    }
160
161    /// Extract a slice
162    pub fn as_slice(&self) -> &[u8] {
163        &self.data[..self.len]
164    }
165
166    /// Extract a mutable slice
167    pub fn as_mut_slice(&mut self) -> &mut [u8] {
168        &mut self.data[..self.len]
169    }
170}
171
172impl core::fmt::Display for ExtensionCapacityError {
173    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
174        write!(f, "extension data exceeds capacity")
175    }
176}
177
178impl core::error::Error for ExtensionCapacityError {}
179
180impl<const N: usize> TryFrom<ExtensionRef<'_>> for FixedExtension<N> {
181    type Error = ExtensionCapacityError;
182
183    fn try_from(value: ExtensionRef<'_>) -> Result<Self, Self::Error> {
184        if value.data.len() > N {
185            return Err(ExtensionCapacityError(()));
186        }
187        let mut buf = [0u8; N];
188        buf[..value.data.len()].copy_from_slice(value.data);
189        Ok(Self {
190            r#type: value.r#type,
191            len: value.data.len(),
192            data: buf,
193        })
194    }
195}
196
197#[cfg(feature = "alloc")]
198mod owned {
199    use super::*;
200
201    /// An owned container for extension payloads.
202    #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
203    pub struct ExtensionOwned {
204        /// Application‑defined extension type code.
205        pub r#type: i8,
206        /// payload bytes.
207        pub data: alloc::vec::Vec<u8>,
208    }
209
210    impl ExtensionOwned {
211        /// Create an owned extension value with the given type code and payload.
212        pub fn new(r#type: i8, data: alloc::vec::Vec<u8>) -> Self {
213            Self { r#type, data }
214        }
215
216        /// Borrow as [`ExtensionRef`] for encoding.
217        pub fn as_ref(&self) -> ExtensionRef<'_> {
218            ExtensionRef {
219                r#type: self.r#type,
220                data: &self.data,
221            }
222        }
223    }
224
225    impl<'a> From<ExtensionRef<'a>> for ExtensionOwned {
226        fn from(value: ExtensionRef<'a>) -> Self {
227            Self {
228                r#type: value.r#type,
229                data: value.data.to_vec(),
230            }
231        }
232    }
233
234    impl<const N: usize> From<FixedExtension<N>> for ExtensionOwned {
235        fn from(value: FixedExtension<N>) -> Self {
236            Self {
237                r#type: value.r#type,
238                data: value.as_slice().to_vec(),
239            }
240        }
241    }
242}
243
244#[cfg(feature = "alloc")]
245pub use owned::ExtensionOwned;