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