1 use super::*;
2 
3 use ciborium_io::Write;
4 
5 /// An encoder for serializing CBOR items
6 ///
7 /// This structure wraps a writer and provides convenience functions for
8 /// writing `Header` objects to the wire.
9 pub struct Encoder<W: Write>(W);
10 
11 impl<W: Write> From<W> for Encoder<W> {
12     #[inline]
from(value: W) -> Self13     fn from(value: W) -> Self {
14         Self(value)
15     }
16 }
17 
18 impl<W: Write> Write for Encoder<W> {
19     type Error = W::Error;
20 
write_all(&mut self, data: &[u8]) -> Result<(), Self::Error>21     fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> {
22         self.0.write_all(data)
23     }
24 
flush(&mut self) -> Result<(), Self::Error>25     fn flush(&mut self) -> Result<(), Self::Error> {
26         self.0.flush()
27     }
28 }
29 
30 impl<W: Write> Encoder<W> {
31     /// Push a `Header` to the wire
32     #[inline]
push(&mut self, header: Header) -> Result<(), W::Error>33     pub fn push(&mut self, header: Header) -> Result<(), W::Error> {
34         let title = Title::from(header);
35 
36         let major = match title.0 {
37             Major::Positive => 0,
38             Major::Negative => 1,
39             Major::Bytes => 2,
40             Major::Text => 3,
41             Major::Array => 4,
42             Major::Map => 5,
43             Major::Tag => 6,
44             Major::Other => 7,
45         };
46 
47         let minor = match title.1 {
48             Minor::This(x) => x,
49             Minor::Next1(..) => 24,
50             Minor::Next2(..) => 25,
51             Minor::Next4(..) => 26,
52             Minor::Next8(..) => 27,
53             Minor::More => 31,
54         };
55 
56         self.0.write_all(&[major << 5 | minor])?;
57         self.0.write_all(title.1.as_ref())
58     }
59 
60     /// Serialize a byte slice as CBOR
61     ///
62     /// Optionally, segment the output into `segment` size segments. Note that
63     /// if `segment == Some(0)` it will be silently upgraded to `Some(1)`. This
64     /// minimum value is highly inefficient and should not be relied upon.
65     #[inline]
bytes( &mut self, value: &[u8], segment: impl Into<Option<usize>>, ) -> Result<(), W::Error>66     pub fn bytes(
67         &mut self,
68         value: &[u8],
69         segment: impl Into<Option<usize>>,
70     ) -> Result<(), W::Error> {
71         let max = segment.into().unwrap_or(value.len());
72         let max = core::cmp::max(max, 1);
73 
74         if max >= value.len() {
75             self.push(Header::Bytes(Some(value.len())))?;
76             self.write_all(value)?;
77         } else {
78             self.push(Header::Bytes(None))?;
79 
80             for chunk in value.chunks(max) {
81                 self.push(Header::Bytes(Some(chunk.len())))?;
82                 self.write_all(chunk)?;
83             }
84 
85             self.push(Header::Break)?;
86         }
87 
88         Ok(())
89     }
90 
91     /// Serialize a string slice as CBOR
92     ///
93     /// Optionally, segment the output into `segment` size segments. Note that
94     /// since care is taken to ensure that each segment is itself a valid UTF-8
95     /// string, if `segment` contains a value of less than 4, it will be
96     /// silently upgraded to 4. This minimum value is highly inefficient and
97     /// should not be relied upon.
98     #[inline]
text(&mut self, value: &str, segment: impl Into<Option<usize>>) -> Result<(), W::Error>99     pub fn text(&mut self, value: &str, segment: impl Into<Option<usize>>) -> Result<(), W::Error> {
100         let max = segment.into().unwrap_or(value.len());
101         let max = core::cmp::max(max, 4);
102 
103         if max >= value.len() {
104             self.push(Header::Text(Some(value.len())))?;
105             self.write_all(value.as_bytes())?;
106         } else {
107             self.push(Header::Text(None))?;
108 
109             let mut bytes = value.as_bytes();
110             while !bytes.is_empty() {
111                 let mut len = core::cmp::min(bytes.len(), max);
112                 while len > 0 && core::str::from_utf8(&bytes[..len]).is_err() {
113                     len -= 1
114                 }
115 
116                 let (prefix, suffix) = bytes.split_at(len);
117                 self.push(Header::Text(Some(prefix.len())))?;
118                 self.write_all(prefix)?;
119                 bytes = suffix;
120             }
121 
122             self.push(Header::Break)?;
123         }
124 
125         Ok(())
126     }
127 }
128