use std::io::{self, Cursor, Read, Write};
use ciborium::value::{CanonicalValue, Value};
use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "fingerprint")]
use crate::{
frame::{encode_header, validate_header, HEADER_LEN},
schema::{config_fingerprint, hash_bytes, hash_u64, Fingerprint},
};
use crate::{Config, Error, Result, TrailingBytes};
#[cfg(feature = "fingerprint")]
const FRAME_MAGIC: [u8; 4] = *b"RBCF";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CborConfig {
base: Config,
deterministic: bool,
}
impl CborConfig {
pub(crate) const fn new(base: Config) -> Self {
Self {
base,
deterministic: false,
}
}
pub const fn with_deterministic_encoding(mut self) -> Self {
self.deterministic = true;
self
}
pub const fn with_preserved_map_order(mut self) -> Self {
self.deterministic = false;
self
}
pub const fn is_deterministic(self) -> bool {
self.deterministic
}
pub const fn base_config(self) -> Config {
self.base
}
#[cfg(feature = "fingerprint")]
pub fn fingerprint<T: Fingerprint + ?Sized>(self) -> u64 {
let hash = config_fingerprint(T::TYPE_FINGERPRINT, self.base);
let hash = hash_bytes(hash, b"format:rfc8949-cbor");
hash_u64(hash, self.deterministic as u64)
}
#[cfg(feature = "fingerprint")]
pub const fn with_fingerprint(self) -> FingerprintedCborConfig {
FingerprintedCborConfig { config: self }
}
#[cfg(feature = "compression")]
pub const fn with_zstd_compression(self, level: i32) -> crate::CompressedConfig {
crate::CompressedConfig::cbor(self, level)
}
#[cfg(feature = "encryption")]
pub fn with_encryption(self, key: crate::EncryptionKey) -> crate::EncryptedConfig {
crate::EncryptedConfig::cbor(self, key)
}
pub fn serialize<T: Serialize + ?Sized>(self, value: &T) -> Result<Vec<u8>> {
let mut output = Vec::new();
self.serialize_into(&mut output, value)?;
Ok(output)
}
pub fn serialize_into<W: Write, T: Serialize + ?Sized>(
self,
writer: W,
value: &T,
) -> Result<()> {
let mut writer = LimitedWriter::new(writer, self.base.limit);
let result = if self.deterministic {
let normalized = normalize(Value::serialized(value).map_err(cbor_error)?)?;
ciborium::into_writer(&normalized, &mut writer)
} else {
ciborium::into_writer(value, &mut writer)
};
match result {
Ok(()) => Ok(()),
Err(_) if writer.exceeded => Err(Error::SizeLimit {
limit: self.base.limit.expect("exceeded only with a limit"),
}),
Err(error) => Err(cbor_error(error)),
}
}
pub fn serialized_size<T: Serialize + ?Sized>(self, value: &T) -> Result<u64> {
let mut counter = Counter { written: 0 };
self.serialize_into(&mut counter, value)?;
Ok(counter.written)
}
pub fn deserialize<T: DeserializeOwned>(self, input: &[u8]) -> Result<T> {
if let Some(limit) = self.base.limit {
if input.len() as u64 > limit {
return Err(Error::SizeLimit { limit });
}
}
let mut cursor = Cursor::new(input);
let value = ciborium::from_reader(&mut cursor).map_err(cbor_error)?;
if self.base.trailing == TrailingBytes::Reject && cursor.position() != input.len() as u64 {
return Err(Error::TrailingBytes {
remaining: input.len() - cursor.position() as usize,
});
}
Ok(value)
}
pub fn deserialize_from<R: Read, T: DeserializeOwned>(self, reader: R) -> Result<T> {
let mut reader = LimitedReader::new(reader, self.base.limit);
match ciborium::from_reader(&mut reader) {
Ok(value) => {
if self.base.trailing == TrailingBytes::Reject {
let trailing = reader.drain_remaining()?;
if trailing != 0 {
return Err(Error::TrailingBytes {
remaining: trailing,
});
}
}
Ok(value)
}
Err(_) if reader.exceeded => Err(Error::SizeLimit {
limit: self.base.limit.expect("exceeded only with a limit"),
}),
Err(error) => Err(cbor_error(error)),
}
}
}
#[cfg(feature = "fingerprint")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FingerprintedCborConfig {
config: CborConfig,
}
#[cfg(feature = "fingerprint")]
impl FingerprintedCborConfig {
pub const fn payload_config(self) -> CborConfig {
self.config
}
pub fn serialize<T: Serialize + Fingerprint + ?Sized>(self, value: &T) -> Result<Vec<u8>> {
let mut output = Vec::new();
self.serialize_into(&mut output, value)?;
Ok(output)
}
pub fn serialize_into<W: Write, T: Serialize + Fingerprint + ?Sized>(
self,
mut writer: W,
value: &T,
) -> Result<()> {
writer.write_all(&encode_header(FRAME_MAGIC, self.config.fingerprint::<T>()))?;
self.config.serialize_into(writer, value)
}
pub fn deserialize<T: DeserializeOwned + Fingerprint>(self, input: &[u8]) -> Result<T> {
let payload = validate_header(FRAME_MAGIC, input, self.config.fingerprint::<T>())?;
self.config.deserialize(payload)
}
pub fn deserialize_from<R: Read, T: DeserializeOwned + Fingerprint>(
self,
mut reader: R,
) -> Result<T> {
let mut header = [0; HEADER_LEN];
reader.read_exact(&mut header)?;
validate_header(FRAME_MAGIC, &header, self.config.fingerprint::<T>())?;
self.config.deserialize_from(reader)
}
pub fn serialized_size<T: Serialize + Fingerprint + ?Sized>(self, value: &T) -> Result<u64> {
self.config
.serialized_size(value)?
.checked_add(HEADER_LEN as u64)
.ok_or(Error::SizeLimit { limit: u64::MAX })
}
}
fn normalize(value: Value) -> Result<Value> {
match value {
Value::Array(values) => values
.into_iter()
.map(normalize)
.collect::<Result<Vec<_>>>()
.map(Value::Array),
Value::Map(entries) => {
let mut entries = entries
.into_iter()
.map(|(key, value)| Ok((normalize(key)?, normalize(value)?)))
.collect::<Result<Vec<_>>>()?;
entries.sort_by(|left, right| canonical_cmp(&left.0, &right.0));
if entries
.windows(2)
.any(|pair| canonical_cmp(&pair[0].0, &pair[1].0).is_eq())
{
return Err(Error::Cbor(
"deterministic maps cannot contain duplicate canonical keys".into(),
));
}
Ok(Value::Map(entries))
}
Value::Tag(tag, value) => Ok(Value::Tag(tag, Box::new(normalize(*value)?))),
scalar => Ok(scalar),
}
}
fn canonical_cmp(left: &Value, right: &Value) -> std::cmp::Ordering {
CanonicalValue::from(left.clone()).cmp(&CanonicalValue::from(right.clone()))
}
fn cbor_error(error: impl std::fmt::Display) -> Error {
Error::Cbor(error.to_string())
}
struct LimitedWriter<W> {
inner: W,
limit: Option<u64>,
written: u64,
exceeded: bool,
}
impl<W> LimitedWriter<W> {
const fn new(inner: W, limit: Option<u64>) -> Self {
Self {
inner,
limit,
written: 0,
exceeded: false,
}
}
}
impl<W: Write> Write for LimitedWriter<W> {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
let amount = u64::try_from(bytes.len())
.map_err(|_| io::Error::other("CBOR write length exceeds u64"))?;
let next = self
.written
.checked_add(amount)
.ok_or_else(|| io::Error::other("CBOR write length overflow"))?;
if self.limit.is_some_and(|limit| next > limit) {
self.exceeded = true;
return Err(io::Error::other("CBOR size limit exceeded"));
}
let written = self.inner.write(bytes)?;
self.written = self
.written
.checked_add(written as u64)
.ok_or_else(|| io::Error::other("CBOR write length overflow"))?;
Ok(written)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
struct LimitedReader<R> {
inner: R,
limit: Option<u64>,
read: u64,
exceeded: bool,
}
impl<R> LimitedReader<R> {
const fn new(inner: R, limit: Option<u64>) -> Self {
Self {
inner,
limit,
read: 0,
exceeded: false,
}
}
}
impl<R: Read> LimitedReader<R> {
fn drain_remaining(&mut self) -> Result<usize> {
let mut trailing = 0usize;
let mut buffer = [0u8; 8 * 1024];
loop {
let read = self.inner.read(&mut buffer)?;
if read == 0 {
return Ok(trailing);
}
trailing = trailing
.checked_add(read)
.ok_or(Error::SizeLimit { limit: u64::MAX })?;
self.read = self
.read
.checked_add(read as u64)
.ok_or(Error::SizeLimit { limit: u64::MAX })?;
if let Some(limit) = self.limit {
if self.read > limit {
self.exceeded = true;
return Err(Error::SizeLimit { limit });
}
}
}
}
}
impl<R: Read> Read for LimitedReader<R> {
fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
let allowed = match self.limit {
Some(limit) if self.read >= limit => {
self.exceeded = true;
return Err(io::Error::other("CBOR size limit exceeded"));
}
Some(limit) => usize::try_from((limit - self.read).min(output.len() as u64))
.map_err(|_| io::Error::other("CBOR read length exceeds usize"))?,
None => output.len(),
};
let read = self.inner.read(&mut output[..allowed])?;
self.read += read as u64;
Ok(read)
}
}
struct Counter {
written: u64,
}
impl Write for Counter {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
self.written = self
.written
.checked_add(bytes.len() as u64)
.ok_or_else(|| io::Error::other("CBOR size overflow"))?;
Ok(bytes.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}