128 lines
3.7 KiB
Rust
128 lines
3.7 KiB
Rust
use super::*;
|
|
|
|
use ciborium_io::Write;
|
|
|
|
/// An encoder for serializing CBOR items
|
|
///
|
|
/// This structure wraps a writer and provides convenience functions for
|
|
/// writing `Header` objects to the wire.
|
|
pub struct Encoder<W: Write>(W);
|
|
|
|
impl<W: Write> From<W> for Encoder<W> {
|
|
#[inline]
|
|
fn from(value: W) -> Self {
|
|
Self(value)
|
|
}
|
|
}
|
|
|
|
impl<W: Write> Write for Encoder<W> {
|
|
type Error = W::Error;
|
|
|
|
fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
|
self.0.write_all(data)
|
|
}
|
|
|
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
|
self.0.flush()
|
|
}
|
|
}
|
|
|
|
impl<W: Write> Encoder<W> {
|
|
/// Push a `Header` to the wire
|
|
#[inline]
|
|
pub fn push(&mut self, header: Header) -> Result<(), W::Error> {
|
|
let title = Title::from(header);
|
|
|
|
let major = match title.0 {
|
|
Major::Positive => 0,
|
|
Major::Negative => 1,
|
|
Major::Bytes => 2,
|
|
Major::Text => 3,
|
|
Major::Array => 4,
|
|
Major::Map => 5,
|
|
Major::Tag => 6,
|
|
Major::Other => 7,
|
|
};
|
|
|
|
let minor = match title.1 {
|
|
Minor::This(x) => x,
|
|
Minor::Next1(..) => 24,
|
|
Minor::Next2(..) => 25,
|
|
Minor::Next4(..) => 26,
|
|
Minor::Next8(..) => 27,
|
|
Minor::More => 31,
|
|
};
|
|
|
|
self.0.write_all(&[major << 5 | minor])?;
|
|
self.0.write_all(title.1.as_ref())
|
|
}
|
|
|
|
/// Serialize a byte slice as CBOR
|
|
///
|
|
/// Optionally, segment the output into `segment` size segments. Note that
|
|
/// if `segment == Some(0)` it will be silently upgraded to `Some(1)`. This
|
|
/// minimum value is highly inefficient and should not be relied upon.
|
|
#[inline]
|
|
pub fn bytes(
|
|
&mut self,
|
|
value: &[u8],
|
|
segment: impl Into<Option<usize>>,
|
|
) -> Result<(), W::Error> {
|
|
let max = segment.into().unwrap_or_else(|| value.len());
|
|
let max = core::cmp::max(max, 1);
|
|
|
|
if max >= value.len() {
|
|
self.push(Header::Bytes(Some(value.len())))?;
|
|
self.write_all(value)?;
|
|
} else {
|
|
self.push(Header::Bytes(None))?;
|
|
|
|
for chunk in value.chunks(max) {
|
|
self.push(Header::Bytes(Some(chunk.len())))?;
|
|
self.write_all(chunk)?;
|
|
}
|
|
|
|
self.push(Header::Break)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Serialize a string slice as CBOR
|
|
///
|
|
/// Optionally, segment the output into `segment` size segments. Note that
|
|
/// since care is taken to ensure that each segment is itself a valid UTF-8
|
|
/// string, if `segment` contains a value of less than 4, it will be
|
|
/// silently upgraded to 4. This minimum value is highly inefficient and
|
|
/// should not be relied upon.
|
|
#[inline]
|
|
pub fn text(&mut self, value: &str, segment: impl Into<Option<usize>>) -> Result<(), W::Error> {
|
|
let max = segment.into().unwrap_or_else(|| value.len());
|
|
let max = core::cmp::max(max, 4);
|
|
|
|
if max >= value.len() {
|
|
self.push(Header::Text(Some(value.len())))?;
|
|
self.write_all(value.as_bytes())?;
|
|
} else {
|
|
self.push(Header::Text(None))?;
|
|
|
|
let mut bytes = value.as_bytes();
|
|
while !bytes.is_empty() {
|
|
let mut len = core::cmp::min(bytes.len(), max);
|
|
while len > 0 && core::str::from_utf8(&bytes[..len]).is_err() {
|
|
len -= 1
|
|
}
|
|
|
|
let (prefix, suffix) = bytes.split_at(len);
|
|
self.push(Header::Text(Some(prefix.len())))?;
|
|
self.write_all(prefix)?;
|
|
bytes = suffix;
|
|
}
|
|
|
|
self.push(Header::Break)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|