Skip to main content

messagepack_core/decode/
str.rs

1//! String decoding helpers.
2
3use super::{DecodeBorrowed, Error, NbyteReader};
4use crate::{Decode, formats::Format, io::IoRead};
5
6/// Decode a MessagePack string and return a borrowed `&str`.
7pub struct StrDecoder;
8
9impl<'de> DecodeBorrowed<'de> for StrDecoder {
10    type Value = &'de str;
11
12    fn decode_borrowed_with_format<R>(
13        format: Format,
14        reader: &mut R,
15    ) -> core::result::Result<Self::Value, Error<R::Error>>
16    where
17        R: IoRead<'de>,
18    {
19        let str_ref = ReferenceStrDecoder::decode_with_format(format, reader)?;
20        match str_ref {
21            ReferenceStr::Borrowed(s) => Ok(s),
22            ReferenceStr::Copied(_) => Err(Error::InvalidData),
23        }
24    }
25}
26
27impl<'de> DecodeBorrowed<'de> for &'de str {
28    type Value = &'de str;
29
30    fn decode_borrowed_with_format<R>(
31        format: Format,
32        reader: &mut R,
33    ) -> core::result::Result<Self::Value, Error<R::Error>>
34    where
35        R: IoRead<'de>,
36    {
37        StrDecoder::decode_borrowed_with_format(format, reader)
38    }
39}
40
41/// Borrowed or copied UTF‑8 string reference
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
43pub enum ReferenceStr<'de, 'a> {
44    /// Borrowed from the input (`'de`).
45    Borrowed(&'de str),
46    /// Copied into a transient buffer bound to `'a`.
47    Copied(&'a str),
48}
49
50impl ReferenceStr<'_, '_> {
51    /// Borrow the underlying string regardless of `Borrowed` or `Copied`.
52    pub const fn as_str(&self) -> &str {
53        match self {
54            ReferenceStr::Borrowed(s) => s,
55            ReferenceStr::Copied(s) => s,
56        }
57    }
58}
59
60impl PartialEq<str> for ReferenceStr<'_, '_> {
61    fn eq(&self, other: &str) -> bool {
62        self.as_str() == other
63    }
64}
65
66impl PartialEq<ReferenceStr<'_, '_>> for str {
67    fn eq(&self, other: &ReferenceStr<'_, '_>) -> bool {
68        other.as_str() == self
69    }
70}
71
72/// Decode a MessagePack string and return a [ReferenceStr]
73pub struct ReferenceStrDecoder;
74
75impl<'de> Decode<'de> for ReferenceStrDecoder {
76    type Value<'a>
77        = ReferenceStr<'de, 'a>
78    where
79        Self: 'a,
80        'de: 'a;
81    fn decode_with_format<'a, R>(
82        format: Format,
83        reader: &'a mut R,
84    ) -> Result<Self::Value<'a>, Error<R::Error>>
85    where
86        R: IoRead<'de>,
87        'de: 'a,
88    {
89        let len = match format {
90            Format::FixStr(n) => n.into(),
91            Format::Str8 => NbyteReader::<1>::read(reader)?,
92            Format::Str16 => NbyteReader::<2>::read(reader)?,
93            Format::Str32 => NbyteReader::<4>::read(reader)?,
94            _ => return Err(Error::UnexpectedFormat),
95        };
96        let data = reader.read_slice(len).map_err(Error::Io)?;
97        match data {
98            crate::io::Reference::Borrowed(items) => {
99                let s = str::from_utf8(items).map_err(|_| Error::InvalidData)?;
100                Ok(ReferenceStr::Borrowed(s))
101            }
102            crate::io::Reference::Copied(items) => {
103                let s = str::from_utf8(items).map_err(|_| Error::InvalidData)?;
104                Ok(ReferenceStr::Copied(s))
105            }
106        }
107    }
108}
109
110/// Decode a Messagepack string without utf-8 validation.
111/// This is useful for lowering the decoding cost.
112pub struct ReferenceStrBinDecoder;
113
114impl<'de> Decode<'de> for ReferenceStrBinDecoder {
115    type Value<'a>
116        = crate::io::Reference<'de, 'a>
117    where
118        Self: 'a,
119        'de: 'a;
120
121    fn decode_with_format<'a, R>(
122        format: Format,
123        reader: &'a mut R,
124    ) -> Result<Self::Value<'a>, Error<R::Error>>
125    where
126        R: IoRead<'de>,
127        'de: 'a,
128    {
129        let len = match format {
130            Format::FixStr(n) => n.into(),
131            Format::Str8 => NbyteReader::<1>::read(reader)?,
132            Format::Str16 => NbyteReader::<2>::read(reader)?,
133            Format::Str32 => NbyteReader::<4>::read(reader)?,
134            _ => return Err(Error::UnexpectedFormat),
135        };
136        reader.read_slice(len).map_err(Error::Io)
137    }
138}
139
140#[cfg(feature = "alloc")]
141mod alloc_impl {
142    use super::*;
143    impl<'de> DecodeBorrowed<'de> for alloc::string::String {
144        type Value = alloc::string::String;
145
146        fn decode_borrowed_with_format<R>(
147            format: Format,
148            reader: &mut R,
149        ) -> core::result::Result<Self::Value, Error<R::Error>>
150        where
151            R: IoRead<'de>,
152        {
153            let sref = ReferenceStrDecoder::decode_with_format(format, reader)?;
154            let owned = match sref {
155                ReferenceStr::Borrowed(s) => alloc::string::String::from(s),
156                ReferenceStr::Copied(s) => alloc::string::String::from(s),
157            };
158            Ok(owned)
159        }
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166    use crate::decode::Decode;
167
168    #[test]
169    fn decode_str() {
170        let buf: &[u8] = &[
171            0xab, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64,
172        ];
173
174        let mut r = crate::io::SliceReader::new(buf);
175        let decoded = StrDecoder::decode(&mut r).unwrap();
176        let expect = "Hello World";
177        assert_eq!(decoded, expect);
178        assert_eq!(r.rest().len(), 0);
179    }
180
181    #[test]
182    fn decode_invalid_str() {
183        let buf: &[u8] = &[0xa2, 0xc3, 0x28];
184        let mut r = crate::io::SliceReader::new(buf);
185        let err = StrDecoder::decode(&mut r).unwrap_err();
186        assert_eq!(err, Error::InvalidData);
187
188        let buf: &[u8] = &[0xa1, 0x80];
189        let mut r = crate::io::SliceReader::new(buf);
190        let err = StrDecoder::decode(&mut r).unwrap_err();
191        assert_eq!(err, Error::InvalidData);
192    }
193
194    #[cfg(feature = "alloc")]
195    #[test]
196    fn decode_string_owned() {
197        let buf: &[u8] = &[0xa3, b'f', b'o', b'o'];
198        let mut r = crate::io::SliceReader::new(buf);
199        let s = <alloc::string::String as Decode>::decode(&mut r).unwrap();
200        assert_eq!(s.as_str(), "foo");
201        assert!(r.rest().is_empty());
202    }
203}