rmp/encode/
mod.rs

1//! Provides various functions and structs for MessagePack encoding.
2
3mod bin;
4mod dec;
5mod ext;
6mod map;
7mod sint;
8mod str;
9mod uint;
10mod vec;
11
12pub use self::bin::{write_bin, write_bin_len};
13pub use self::dec::{write_f32, write_f64};
14pub use self::sint::{write_i16, write_i32, write_i64, write_i8, write_nfix, write_sint};
15pub use self::str::{write_str, write_str_len};
16pub use self::uint::{write_pfix, write_u16, write_u32, write_u64, write_u8, write_uint, write_uint8};
17
18use core::fmt::{self, Debug, Display, Formatter};
19#[cfg(feature = "std")]
20use std::error;
21
22use crate::Marker;
23
24pub mod buffer;
25pub use buffer::ByteBuf;
26
27#[doc(inline)]
28#[allow(deprecated)]
29pub use crate::errors::Error;
30
31/// The error type for operations on the [`RmpWrite`] trait.
32///
33/// For [`std::io::Write`], this is [`std::io::Error`]
34/// For [`ByteBuf`], this is [`core::convert::Infallible`]
35pub trait RmpWriteErr: Display + Debug + crate::errors::MaybeErrBound + 'static {}
36#[cfg(feature = "std")]
37impl RmpWriteErr for std::io::Error {}
38impl RmpWriteErr for core::convert::Infallible {}
39
40// An error returned from the `write_marker` and `write_fixval` functions.
41struct MarkerWriteError<E: RmpWriteErr>(E);
42
43impl<E: RmpWriteErr> From<E> for MarkerWriteError<E> {
44    #[cold]
45    fn from(err: E) -> Self {
46        MarkerWriteError(err)
47    }
48}
49
50/// Attempts to write the given marker into the writer.
51fn write_marker<W: RmpWrite>(wr: &mut W, marker: Marker) -> Result<(), MarkerWriteError<W::Error>> {
52    wr.write_u8(marker.to_u8()).map_err(MarkerWriteError)
53}
54
55/// An error returned from primitive values write functions.
56#[doc(hidden)]
57pub struct DataWriteError<E: RmpWriteErr>(E);
58
59impl<E: RmpWriteErr> From<E> for DataWriteError<E> {
60    #[cold]
61    #[inline]
62    fn from(err: E) -> DataWriteError<E> {
63        DataWriteError(err)
64    }
65}
66
67/// Encodes and attempts to write a nil value into the given write.
68///
69/// According to the MessagePack specification, a nil value is represented as a single `0xc0` byte.
70///
71/// # Errors
72///
73/// This function will return `Error` on any I/O error occurred while writing the nil marker.
74///
75/// # Examples
76///
77/// ```
78/// let mut buf = Vec::new();
79///
80/// rmp::encode::write_nil(&mut buf).unwrap();
81///
82/// assert_eq!(vec![0xc0], buf);
83/// ```
84#[inline]
85pub fn write_nil<W: RmpWrite>(wr: &mut W) -> Result<(), W::Error> {
86    write_marker(wr, Marker::Null).map_err(|e| e.0)
87}
88
89/// Encodes and attempts to write a bool value into the given write.
90///
91/// According to the MessagePack specification, an encoded boolean value is represented as a single
92/// byte.
93///
94/// # Errors
95///
96/// Each call to this function may generate an I/O error indicating that the operation could not be
97/// completed.
98#[inline]
99pub fn write_bool<W: RmpWrite>(wr: &mut W, val: bool) -> Result<(), W::Error> {
100    let marker = if val { Marker::True } else { Marker::False };
101
102    write_marker(wr, marker).map_err(|e| e.0)
103}
104
105mod sealed {
106    pub trait Sealed {}
107    #[cfg(feature = "std")]
108    impl<T: ?Sized + std::io::Write> Sealed for T {}
109    #[cfg(not(feature = "std"))]
110    impl Sealed for &mut [u8] {}
111    #[cfg(not(feature = "std"))]
112    impl Sealed for alloc::vec::Vec<u8> {}
113    impl Sealed for super::ByteBuf {}
114}
115
116macro_rules! write_byteorder_utils {
117    ($($name:ident => $tp:ident),* $(,)?) => {
118        $(
119            #[inline]
120            #[doc(hidden)]
121            fn $name(&mut self, val: $tp) -> Result<(), DataWriteError<Self::Error>> where Self: Sized {
122                const SIZE: usize = core::mem::size_of::<$tp>();
123                let mut buf: [u8; SIZE] = [0u8; SIZE];
124                paste::paste! {
125                    <byteorder::BigEndian as byteorder::ByteOrder>::[<write_ $tp>](&mut buf, val);
126                }
127                self.write_bytes(&buf).map_err(DataWriteError)
128            }
129        )*
130    };
131}
132
133/// A type that `rmp` supports writing into.
134///
135/// The methods of this trait should be considered an implementation detail (for now).
136/// It is currently sealed (can not be implemented by the user).
137///
138/// See also [`std::io::Write`] and [`byteorder::WriteBytesExt`]
139///
140/// Its primary implementations are [`std::io::Write`] and [`ByteBuf`].
141pub trait RmpWrite: sealed::Sealed {
142    type Error: RmpWriteErr;
143
144    /// Write a single byte to this stream
145    #[inline]
146    fn write_u8(&mut self, val: u8) -> Result<(), Self::Error> {
147        let buf = [val];
148        self.write_bytes(&buf)
149    }
150
151    /// Write a slice of bytes to the underlying stream
152    ///
153    /// This will either write all the bytes or return an error.
154    /// See also [`std::io::Write::write_all`]
155    fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Self::Error>;
156
157    // Internal helper functions to map I/O error into the `DataWriteError` error.
158
159    /// Write a single (signed) byte to this stream.
160    #[inline]
161    #[doc(hidden)]
162    fn write_data_u8(&mut self, val: u8) -> Result<(), DataWriteError<Self::Error>> {
163        self.write_u8(val).map_err(DataWriteError)
164    }
165    /// Write a single (signed) byte to this stream.
166    #[inline]
167    #[doc(hidden)]
168    fn write_data_i8(&mut self, val: i8) -> Result<(), DataWriteError<Self::Error>> {
169        self.write_data_u8(val as u8)
170    }
171
172    write_byteorder_utils!(
173        write_data_u16 => u16,
174        write_data_u32 => u32,
175        write_data_u64 => u64,
176        write_data_i16 => i16,
177        write_data_i32 => i32,
178        write_data_i64 => i64,
179        write_data_f32 => f32,
180        write_data_f64 => f64
181    );
182}
183
184#[cfg(feature = "std")]
185impl<T: std::io::Write> RmpWrite for T {
186    type Error = std::io::Error;
187
188    #[inline]
189    fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
190        self.write_all(buf)
191    }
192}
193
194/// An error that can occur when attempting to write multi-byte MessagePack value.
195#[derive(Debug)]
196#[allow(deprecated)] // TODO: Needed for compatibility
197pub enum ValueWriteError<E: RmpWriteErr = Error> {
198    /// I/O error while writing marker.
199    InvalidMarkerWrite(E),
200    /// I/O error while writing data.
201    InvalidDataWrite(E),
202}
203
204impl<E: RmpWriteErr> From<MarkerWriteError<E>> for ValueWriteError<E> {
205    #[cold]
206    fn from(err: MarkerWriteError<E>) -> Self {
207        match err {
208            MarkerWriteError(err) => ValueWriteError::InvalidMarkerWrite(err),
209        }
210    }
211}
212
213impl<E: RmpWriteErr> From<DataWriteError<E>> for ValueWriteError<E> {
214    #[cold]
215    fn from(err: DataWriteError<E>) -> Self {
216        match err {
217            DataWriteError(err) => ValueWriteError::InvalidDataWrite(err),
218        }
219    }
220}
221
222#[cfg(feature = "std")] // Backwards compatbility ;)
223impl From<ValueWriteError<std::io::Error>> for std::io::Error {
224    #[cold]
225    fn from(err: ValueWriteError<std::io::Error>) -> std::io::Error {
226        match err {
227            ValueWriteError::InvalidMarkerWrite(err) |
228            ValueWriteError::InvalidDataWrite(err) => err,
229        }
230    }
231}
232
233#[cfg(feature = "std")]
234impl<E: RmpWriteErr> error::Error for ValueWriteError<E> {
235    #[cold]
236    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
237        match *self {
238            ValueWriteError::InvalidMarkerWrite(ref err) |
239            ValueWriteError::InvalidDataWrite(ref err) => Some(err),
240        }
241    }
242}
243
244impl<E: RmpWriteErr> Display for ValueWriteError<E> {
245    #[cold]
246    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
247        f.write_str("error while writing multi-byte MessagePack value")
248    }
249}
250
251/// Encodes and attempts to write the most efficient array length implementation to the given write,
252/// returning the marker used.
253///
254/// # Errors
255///
256/// This function will return `ValueWriteError` on any I/O error occurred while writing either the
257/// marker or the data.
258pub fn write_array_len<W: RmpWrite>(wr: &mut W, len: u32) -> Result<Marker, ValueWriteError<W::Error>> {
259    let marker = if len < 16 {
260        Marker::FixArray(len as u8)
261    } else if len <= u16::MAX as u32 {
262        Marker::Array16
263    } else {
264        Marker::Array32
265    };
266
267    write_marker(wr, marker)?;
268    if marker == Marker::Array16 {
269        wr.write_data_u16(len as u16)?;
270    } else if marker == Marker::Array32 {
271        wr.write_data_u32(len)?;
272    }
273    Ok(marker)
274}
275
276/// Encodes and attempts to write the most efficient map length implementation to the given write,
277/// returning the marker used.
278///
279/// # Errors
280///
281/// This function will return `ValueWriteError` on any I/O error occurred while writing either the
282/// marker or the data.
283pub fn write_map_len<W: RmpWrite>(wr: &mut W, len: u32) -> Result<Marker, ValueWriteError<W::Error>> {
284    let marker = if len < 16 {
285        Marker::FixMap(len as u8)
286    } else if len <= u16::MAX as u32 {
287        Marker::Map16
288    } else {
289        Marker::Map32
290    };
291
292    write_marker(wr, marker)?;
293    if marker == Marker::Map16 {
294        wr.write_data_u16(len as u16)?;
295    } else if marker == Marker::Map32 {
296        wr.write_data_u32(len)?;
297    }
298    Ok(marker)
299}
300
301/// Encodes and attempts to write the most efficient ext metadata implementation to the given
302/// write, returning the marker used.
303///
304/// # Errors
305///
306/// This function will return `ValueWriteError` on any I/O error occurred while writing either the
307/// marker or the data.
308///
309/// # Panics
310///
311/// Panics if `ty` is negative, because it is reserved for future MessagePack extension including
312/// 2-byte type information.
313pub fn write_ext_meta<W: RmpWrite>(wr: &mut W, len: u32, ty: i8) -> Result<Marker, ValueWriteError<W::Error>> {
314    let marker = match len {
315        1 => Marker::FixExt1,
316        2 => Marker::FixExt2,
317        4 => Marker::FixExt4,
318        8 => Marker::FixExt8,
319        16 => Marker::FixExt16,
320        0..=255 => Marker::Ext8,
321        256..=65535 => Marker::Ext16,
322        _ => Marker::Ext32,
323    };
324    write_marker(wr, marker)?;
325
326    if marker == Marker::Ext8 {
327        wr.write_data_u8(len as u8)?;
328    } else if marker == Marker::Ext16 {
329        wr.write_data_u16(len as u16)?;
330    } else if marker == Marker::Ext32 {
331        wr.write_data_u32(len)?;
332    }
333
334    wr.write_data_i8(ty)?;
335
336    Ok(marker)
337}