use std::collections::BTreeMap;
use cbor2::{cbor, Value};
use serde::{Deserialize, Serialize};
fn assert_size<T: ?Sized + Serialize>(value: &T) {
let actual = cbor2::to_vec(value).unwrap().len() as u64;
assert_eq!(cbor2::serialized_size(value).unwrap(), actual);
}
#[derive(Serialize, Deserialize)]
struct Struct {
a: u8,
b: String,
}
#[derive(Serialize)]
enum Enum {
Unit,
Newtype(u32),
Tuple(u32, u32),
Struct { x: u8 },
}
#[test]
fn integers() {
for v in [
0u64,
23,
24,
255,
256,
65_535,
65_536,
u32::MAX as u64,
u32::MAX as u64 + 1,
u64::MAX,
] {
assert_size(&v);
}
for v in [
-1i64,
-24,
-25,
-256,
-257,
-65_536,
-65_537,
i32::MIN as i64,
i64::MIN,
i64::MAX,
] {
assert_size(&v);
}
assert_size(&7u8);
assert_size(&-7i8);
assert_size(&700u16);
assert_size(&-700i16);
assert_size(&70_000u32);
assert_size(&-70_000i32);
for v in [0u128, u64::MAX as u128, u64::MAX as u128 + 1, u128::MAX] {
assert_size(&v);
}
for v in [
0i128,
i64::MIN as i128,
-(u64::MAX as i128) - 1,
-(u64::MAX as i128) - 2,
i128::MIN,
i128::MAX,
] {
assert_size(&v);
}
}
#[test]
fn floats() {
for v in [
0.0f64,
-0.0,
1.0,
1.5,
1.1,
65504.0,
100000.0,
1.0e300,
5.960464477539063e-8,
f64::INFINITY,
f64::NEG_INFINITY,
f64::NAN,
f64::from_bits(0x7ff8_dead_beef_0000), -f64::NAN,
] {
assert_size(&v);
}
assert_size(&1.5f32);
assert_size(&f32::NAN);
}
#[test]
fn strings_and_bytes() {
for len in [0usize, 1, 23, 24, 255, 256, 65_536] {
assert_size(&"a".repeat(len));
assert_size(&serde_bytes::ByteBuf::from(vec![0xab; len]));
}
for c in ['a', 'ß', '水', '🦀'] {
assert_size(&c);
}
}
#[test]
fn containers() {
assert_size(&Option::<u8>::None);
assert_size(&Some(42u8));
assert_size(&());
assert_size(&(0..30).collect::<Vec<u64>>());
assert_size(&(1u8, "hi", 3u64));
assert_size(&BTreeMap::from([("a", 1u8), ("long_key", 2)]));
assert_size(&Struct {
a: 7,
b: "hello".into(),
});
assert_size(&Enum::Unit);
assert_size(&Enum::Newtype(123));
assert_size(&Enum::Tuple(1, 700));
assert_size(&Enum::Struct { x: 9 });
}
#[test]
fn tags_and_values() {
use cbor2::tag::{AllowAny, RequireExact};
assert_size(&RequireExact::<_, 42>("tagged"));
assert_size(&AllowAny(Some(0x1_0000), 123u64));
assert_size(&AllowAny(None, "plain"));
let value = cbor!({
"big" => u128::MAX,
"nested" => [1, 2.5, null, {"k" => true}],
})
.unwrap();
assert_size(&Value::Tag(99, Box::new(value)));
}
struct UnsizedSeq;
impl Serialize for UnsizedSeq {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_seq((1u8..=3).filter(|_| true))
}
}
#[test]
fn indefinite_containers() {
assert_size(&UnsizedSeq);
}
struct Displayed;
impl std::fmt::Display for Displayed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "id-{}-水", 42)
}
}
impl Serialize for Displayed {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
#[test]
fn collected_strings() {
assert_size(&Displayed);
assert_eq!(
cbor2::to_vec(&Displayed).unwrap(),
cbor2::to_vec(&"id-42-水").unwrap()
);
let back: String = cbor2::from_slice(&cbor2::to_vec(&Displayed).unwrap()).unwrap();
assert_eq!(back, "id-42-水");
}
struct Shifty(std::cell::Cell<usize>, &'static [&'static str]);
impl std::fmt::Display for Shifty {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let n = self.0.get();
self.0.set(n + 1);
f.write_str(self.1[n.min(self.1.len() - 1)])
}
}
impl Serialize for Shifty {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
struct FailingDisplay;
impl std::fmt::Display for FailingDisplay {
fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Err(std::fmt::Error)
}
}
impl Serialize for FailingDisplay {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
#[test]
fn misbehaving_display_is_rejected() {
let grow = Shifty(std::cell::Cell::new(0), &["ab", "abcd"]);
assert!(matches!(
cbor2::to_vec(&grow),
Err(cbor2::ser::Error::Value(..))
));
let shrink = Shifty(std::cell::Cell::new(0), &["abcd", "ab"]);
assert!(matches!(
cbor2::to_vec(&shrink),
Err(cbor2::ser::Error::Value(..))
));
assert!(matches!(
cbor2::to_vec(&FailingDisplay),
Err(cbor2::ser::Error::Value(..))
));
struct Limited(usize);
impl std::io::Write for Limited {
fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
if self.0 == 0 {
return Err(std::io::Error::other("limit"));
}
let n = self.0.min(data.len());
self.0 -= n;
Ok(n)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
assert!(matches!(
cbor2::to_writer(&Displayed, Limited(1)),
Err(cbor2::ser::Error::Io(..))
));
assert!(matches!(
cbor2::to_writer(&Displayed, Limited(0)),
Err(cbor2::ser::Error::Io(..))
));
}
#[test]
fn errors_propagate() {
struct Boom;
impl Serialize for Boom {
fn serialize<S: serde::Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
Err(serde::ser::Error::custom("boom"))
}
}
assert!(cbor2::serialized_size(&Boom).is_err());
}