#![doc = include_str!("../crate-docs.md")]
#![forbid(unsafe_code)]
#![warn(
clippy::cargo,
missing_docs,
// clippy::missing_docs_in_private_items,
clippy::nursery,
clippy::pedantic,
future_incompatible,
rust_2018_idioms,
)]
#![allow(
clippy::missing_errors_doc, // TODO clippy::missing_errors_doc
clippy::option_if_let_else,
clippy::used_underscore_binding, // false positive with tracing
)]
pub mod de;
mod error;
pub mod format;
pub mod reader;
pub mod ser;
mod value;
use std::io::Read;
use byteorder::WriteBytesExt;
pub use self::{error::Error, value::Value};
pub type Result<T> = std::result::Result<T, Error>;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::reader::IoReader;
pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
where
T: Serialize,
{
Config::default().serialize(value)
}
pub fn to_writer<T, W>(value: &T, writer: W) -> Result<()>
where
T: Serialize,
W: WriteBytesExt,
{
Config::default().serialize_into(value, writer)
}
pub fn from_slice<'a, T>(serialized: &'a [u8]) -> Result<T>
where
T: Deserialize<'a>,
{
Config::default().deserialize(serialized)
}
pub fn from_reader<T, R>(reader: R) -> Result<T>
where
T: DeserializeOwned,
R: Read,
{
Config::default().deserialize_from(reader)
}
#[must_use]
#[derive(Clone, Debug)]
pub struct Config {
allocation_budget: usize,
}
impl Default for Config {
fn default() -> Self {
Self {
allocation_budget: usize::MAX,
}
}
}
impl Config {
pub const fn allocation_budget(mut self, budget: usize) -> Self {
self.allocation_budget = budget;
self
}
pub fn deserialize<'de, T>(&self, serialized: &'de [u8]) -> Result<T>
where
T: Deserialize<'de>,
{
let mut deserializer = de::Deserializer::from_slice(serialized, self.allocation_budget)?;
let t = T::deserialize(&mut deserializer)?;
if deserializer.end_of_input() {
Ok(t)
} else {
Err(Error::TrailingBytes)
}
}
pub fn deserialize_from<T, R: Read>(&self, reader: R) -> Result<T>
where
T: DeserializeOwned,
{
let mut deserializer =
de::Deserializer::from_read(IoReader::new(reader), self.allocation_budget)?;
T::deserialize(&mut deserializer)
}
#[allow(clippy::unused_self)]
pub fn serialize<T: Serialize>(&self, value: &T) -> Result<Vec<u8>> {
let mut output = Vec::default();
let mut serializer = ser::Serializer::new(&mut output)?;
value.serialize(&mut serializer)?;
Ok(output)
}
#[allow(clippy::unused_self)]
pub fn serialize_into<T, W>(&self, value: &T, writer: W) -> Result<()>
where
T: Serialize,
W: WriteBytesExt,
{
let mut serializer = ser::Serializer::new(writer)?;
value.serialize(&mut serializer)
}
}
#[cfg(test)]
mod tests {
use std::{borrow::Cow, marker::PhantomData};
use serde::{Deserializer, Serializer};
use serde_json::{value::Value as JsonValue, Number};
use super::*;
use crate::{
format::{Float, Integer, CURRENT_VERSION},
value::Value,
};
fn init_tracing() {
drop(
tracing_subscriber::fmt()
.pretty()
.with_max_level(tracing::Level::TRACE)
.with_span_events(tracing_subscriber::fmt::format::FmtSpan::ENTER)
.try_init(),
);
}
fn test_serialization<S: Serialize + for<'de> Deserialize<'de> + PartialEq + Debug>(
value: &S,
check_length: Option<usize>,
) {
test_serialization_with(value, check_length, |value, deserialized| {
assert_eq!(value, deserialized);
});
}
fn test_serialization_with<
S: Serialize + for<'de> Deserialize<'de> + PartialEq + Debug,
F: FnMut(&S, &S),
>(
value: &S,
check_length: Option<usize>,
mut callback: F,
) {
init_tracing();
let bytes = to_vec(&value).unwrap();
println!("{:?}: {:02x?}", value, bytes);
let deserialized = from_slice::<S>(&bytes).unwrap();
callback(value, &deserialized);
if let Some(check_length) = check_length {
assert_eq!(bytes.len() - 4, check_length);
}
let mut bytes = Vec::new();
to_writer(value, &mut bytes).unwrap();
println!("{:?}: {:02x?}", value, bytes);
let deserialized = from_reader(&bytes[..]).unwrap();
callback(value, &deserialized);
}
use std::fmt::Debug;
#[derive(Serialize, PartialEq, Deserialize, Debug, Default)]
struct NumbersStruct {
u8: u8,
u16: u16,
char: char,
u32: u32,
u64: u64,
u128: u128,
i8: i8,
i16: i16,
i32: i32,
i64: i64,
i128: i128,
f32: f32,
f64: f64,
}
#[derive(Serialize, PartialEq, Deserialize, Debug)]
enum EnumVariants {
Unit,
Tuple(u64),
TupleTwoArgs(u64, u64),
Struct { arg: u64 },
}
#[test]
fn numbers() {
test_serialization(&NumbersStruct::default(), None);
test_serialization(
&NumbersStruct {
u8: u8::MAX,
u16: u16::MAX,
char: char::MAX,
u32: u32::MAX,
u64: u64::MAX,
u128: u128::MAX,
i8: i8::MIN,
i16: i16::MIN,
i32: i32::MIN,
i64: i64::MIN,
i128: i128::MIN,
f32: 1.,
f64: 1.,
},
None,
);
}
#[test]
fn number_packing() {
test_serialization(&0_u128, Some(2));
test_serialization(&(2_u128.pow(8) - 1), Some(2));
test_serialization(&2_u128.pow(8), Some(3));
test_serialization(&(2_u128.pow(16) - 1), Some(3));
test_serialization(&2_u128.pow(16), Some(4));
test_serialization(&(2_u128.pow(24) - 1), Some(4));
test_serialization(&2_u128.pow(24), Some(5));
test_serialization(&(2_u128.pow(32) - 1), Some(5));
test_serialization(&2_u128.pow(32), Some(7));
test_serialization(&(2_u128.pow(48) - 1), Some(7));
test_serialization(&2_u128.pow(48), Some(9));
test_serialization(&(2_u128.pow(64) - 1), Some(9));
test_serialization(&2_u128.pow(64), Some(17));
test_serialization(&0_i128, Some(2));
test_serialization(&(2_i128.pow(7) - 1), Some(2));
test_serialization(&2_i128.pow(7), Some(3));
test_serialization(&(2_i128.pow(15) - 1), Some(3));
test_serialization(&2_i128.pow(15), Some(4));
test_serialization(&(2_i128.pow(23) - 1), Some(4));
test_serialization(&2_i128.pow(23), Some(5));
test_serialization(&(2_i128.pow(31) - 1), Some(5));
test_serialization(&2_i128.pow(31), Some(7));
test_serialization(&(2_i128.pow(47) - 1), Some(7));
test_serialization(&2_i128.pow(47), Some(9));
test_serialization(&-(2_i128.pow(7)), Some(2));
test_serialization(&-(2_i128.pow(7) + 1), Some(3));
test_serialization(&-(2_i128.pow(15)), Some(3));
test_serialization(&-(2_i128.pow(15) + 1), Some(4));
test_serialization(&-(2_i128.pow(23)), Some(4));
test_serialization(&-(2_i128.pow(23) + 1), Some(5));
test_serialization(&-(2_i128.pow(31)), Some(5));
test_serialization(&-(2_i128.pow(31) + 1), Some(7));
test_serialization(&-(2_i128.pow(47)), Some(7));
test_serialization(&-(2_i128.pow(47) + 1), Some(9));
test_serialization(&-(2_i128.pow(63)), Some(9));
test_serialization(&-(2_i128.pow(63) + 1), Some(17));
test_serialization(&f64::INFINITY, Some(3));
test_serialization(&f64::NEG_INFINITY, Some(3));
test_serialization(&0_f64, Some(3));
test_serialization(&-0_f64, Some(3));
test_serialization(&0.1_f64, Some(9));
test_serialization(&0.1_f32, Some(5));
}
#[test]
fn enums() {
test_serialization(&EnumVariants::Unit, None);
test_serialization(&EnumVariants::Tuple(0), None);
test_serialization(&EnumVariants::TupleTwoArgs(1, 2), None);
test_serialization(&EnumVariants::Struct { arg: 3 }, None);
test_serialization(&Some(EnumVariants::Unit), None);
}
#[test]
fn vectors() {
test_serialization(&vec![0_u64, 1], None);
test_serialization(
&vec![NumbersStruct::default(), NumbersStruct::default()],
None,
);
}
#[test]
fn option() {
test_serialization(&Option::<u64>::None, None);
test_serialization(&Some(0_u64), None);
test_serialization(&Some(u64::MAX), None);
}
#[test]
fn phantom() {
test_serialization(&PhantomData::<u64>, None);
}
#[derive(Serialize, PartialEq, Deserialize, Debug, Default)]
struct StringsAndBytes<'a> {
bytes: Cow<'a, [u8]>,
#[serde(with = "serde_bytes")]
bytes_borrowed: Cow<'a, [u8]>,
#[serde(with = "serde_bytes")]
serde_bytes_byte_slice: &'a [u8],
#[serde(with = "serde_bytes")]
serde_bytes_byte_vec: Vec<u8>,
str_ref: &'a str,
string: String,
}
#[test]
fn borrowing_data() {
let original = StringsAndBytes {
bytes: Cow::Borrowed(b"hello"),
bytes_borrowed: Cow::Borrowed(b"hello"),
serde_bytes_byte_slice: b"hello",
serde_bytes_byte_vec: b"world".to_vec(),
str_ref: "hello",
string: String::from("world"),
};
let serialized = to_vec(&original).unwrap();
let deserialized = from_slice(&serialized).unwrap();
assert_eq!(original, deserialized);
assert!(matches!(deserialized.bytes_borrowed, Cow::Borrowed(_)));
}
#[test]
fn limiting_input() {
let original = StringsAndBytes {
bytes: Cow::Borrowed(b"hello"),
bytes_borrowed: Cow::Borrowed(b"hello"),
serde_bytes_byte_slice: b"hello",
serde_bytes_byte_vec: b"world".to_vec(),
str_ref: "hello",
string: String::from("world"),
};
let serialized = to_vec(&original).unwrap();
assert!(Config::default()
.allocation_budget(30)
.deserialize::<StringsAndBytes<'_>>(&serialized)
.is_ok());
assert!(Config::default()
.allocation_budget(29)
.deserialize::<StringsAndBytes<'_>>(&serialized)
.is_err());
let serialized = to_vec(&NumbersStruct {
u8: u8::MAX,
u16: u16::MAX,
char: char::MAX,
u32: u32::MAX,
u64: u64::MAX,
u128: u128::MAX,
i8: i8::MIN,
i16: i16::MIN,
i32: i32::MIN,
i64: i64::MIN,
i128: i128::MIN,
f32: f32::MAX,
f64: f64::MIN,
})
.unwrap();
assert!(Config::default()
.allocation_budget(78)
.deserialize::<NumbersStruct>(&serialized)
.is_ok());
assert!(Config::default()
.allocation_budget(77)
.deserialize::<NumbersStruct>(&serialized)
.is_err());
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct TupleStruct(u32, u8);
#[test]
fn tuple_struct() {
test_serialization(&TupleStruct(1, 2), None);
}
#[test]
fn json_value() {
test_serialization(&JsonValue::Null, None);
test_serialization(&JsonValue::Bool(false), None);
test_serialization(&JsonValue::Bool(true), None);
test_serialization(
&JsonValue::Array(vec![serde_json::value::Value::Null]),
None,
);
test_serialization(&JsonValue::Number(Number::from_f64(1.).unwrap()), None);
test_serialization(&JsonValue::String(String::from("Hello world")), None);
test_serialization(
&JsonValue::Object(
[(String::from("key"), JsonValue::Bool(true))]
.into_iter()
.collect(),
),
None,
);
}
#[test]
fn value() {
macro_rules! roundtrip {
($value:expr) => {{
assert_eq!(
from_slice::<Value<'_>>(&to_vec(&$value).unwrap()).unwrap(),
$value
);
}};
}
roundtrip!(Value::None);
roundtrip!(Value::Unit);
roundtrip!(Value::Bool(true));
roundtrip!(Value::Integer(Integer::from(i8::MAX)));
roundtrip!(Value::Integer(Integer::from(i16::MAX)));
roundtrip!(Value::Integer(Integer::from(i32::MAX)));
roundtrip!(Value::Integer(Integer::from(i64::MAX)));
roundtrip!(Value::Integer(Integer::from(i128::MAX)));
roundtrip!(Value::Integer(Integer::from(u8::MAX)));
roundtrip!(Value::Integer(Integer::from(u16::MAX)));
roundtrip!(Value::Integer(Integer::from(u32::MAX)));
roundtrip!(Value::Integer(Integer::from(u64::MAX)));
roundtrip!(Value::Integer(Integer::from(u128::MAX)));
roundtrip!(Value::Float(Float::from(std::f64::consts::PI)));
roundtrip!(Value::Float(Float::from(std::f32::consts::PI)));
roundtrip!(Value::Sequence(vec![Value::None]));
roundtrip!(Value::Mappings(vec![(Value::None, Value::Unit)]));
let original_value = Value::Bytes(Cow::Borrowed(b"hello"));
let encoded_bytes = to_vec(&original_value).unwrap();
let borrowed_decoded: Value<'_> = from_slice(&encoded_bytes).unwrap();
assert_eq!(Value::String(Cow::Borrowed("hello")), borrowed_decoded);
assert!(matches!(borrowed_decoded, Value::String(Cow::Borrowed(_))));
let original_value = Value::Bytes(Cow::Borrowed(b"\xFE\xED\xD0\xD0"));
let encoded_bytes = to_vec(&original_value).unwrap();
let borrowed_decoded: Value<'_> = from_slice(&encoded_bytes).unwrap();
assert_eq!(
Value::Bytes(Cow::Borrowed(b"\xFE\xED\xD0\xD0")),
borrowed_decoded
);
assert!(matches!(borrowed_decoded, Value::Bytes(Cow::Borrowed(_))));
}
#[test]
fn incompatible_version() {
let mut incompatible_header = Vec::new();
format::write_header(&mut incompatible_header, CURRENT_VERSION + 1).unwrap();
assert!(matches!(
from_slice::<()>(&incompatible_header),
Err(Error::IncompatibleVersion)
));
}
#[test]
fn invalid_char_cast() {
let bytes = to_vec(&0x11_0000_u32).unwrap();
assert!(matches!(
from_slice::<char>(&bytes),
Err(Error::InvalidUtf8(_))
));
}
#[test]
fn bytes_to_identifier() {
let mut valid_bytes = Vec::new();
format::write_header(&mut valid_bytes, CURRENT_VERSION).unwrap();
format::write_named(&mut valid_bytes).unwrap();
format::write_bytes(&mut valid_bytes, b"Unit").unwrap();
assert_eq!(
from_slice::<EnumVariants>(&valid_bytes).unwrap(),
EnumVariants::Unit
);
let mut invalid_bytes = Vec::new();
format::write_header(&mut invalid_bytes, CURRENT_VERSION).unwrap();
format::write_named(&mut invalid_bytes).unwrap();
format::write_bytes(&mut invalid_bytes, &0xFFFF_FFFF_u32.to_be_bytes()).unwrap();
assert!(matches!(
from_slice::<EnumVariants>(&invalid_bytes),
Err(Error::InvalidUtf8(_))
));
}
#[test]
fn invalid_symbol() {
let mut valid_bytes = Vec::new();
format::write_header(&mut valid_bytes, CURRENT_VERSION).unwrap();
format::write_atom_header(&mut valid_bytes, format::Kind::Symbol, Some(4)).unwrap();
format::write_bytes(&mut valid_bytes, &0xFFFF_FFFF_u32.to_be_bytes()).unwrap();
assert!(matches!(
from_slice::<Value<'_>>(&valid_bytes),
Err(Error::InvalidUtf8(_))
));
}
#[test]
fn unknown_special() {
let mut invalid_bytes = Vec::new();
format::write_header(&mut invalid_bytes, CURRENT_VERSION).unwrap();
format::write_atom_header(
&mut invalid_bytes,
format::Kind::Special,
Some(format::SPECIAL_COUNT),
)
.unwrap();
assert!(matches!(from_slice::<()>(&invalid_bytes), Err(_)));
}
#[test]
fn invalid_numbers() {
let mut invalid_float_byte_len = Vec::new();
format::write_header(&mut invalid_float_byte_len, CURRENT_VERSION).unwrap();
format::write_atom_header(&mut invalid_float_byte_len, format::Kind::Float, Some(0))
.unwrap();
assert!(matches!(from_slice::<f32>(&invalid_float_byte_len), Err(_)));
assert!(matches!(
format::Float::read_from(format::Kind::Symbol, 0, &mut &invalid_float_byte_len[..]),
Err(_)
));
let mut invalid_signed_byte_len = Vec::new();
format::write_header(&mut invalid_signed_byte_len, CURRENT_VERSION).unwrap();
format::write_atom_header(&mut invalid_signed_byte_len, format::Kind::Int, Some(10))
.unwrap();
assert!(matches!(
from_slice::<i32>(&invalid_signed_byte_len),
Err(_)
));
assert!(matches!(
format::Integer::read_from(format::Kind::Symbol, 0, &mut &invalid_signed_byte_len[..]),
Err(_)
));
let mut invalid_unsigned_byte_len = Vec::new();
format::write_header(&mut invalid_unsigned_byte_len, CURRENT_VERSION).unwrap();
format::write_atom_header(&mut invalid_unsigned_byte_len, format::Kind::UInt, Some(10))
.unwrap();
assert!(matches!(
from_slice::<u32>(&invalid_unsigned_byte_len),
Err(_)
));
}
#[test]
#[allow(clippy::unnecessary_mut_passed)] fn not_human_readable() {
let mut bytes = Vec::new();
let mut serializer = ser::Serializer::new(&mut bytes).unwrap();
assert!(!(&mut serializer).is_human_readable());
().serialize(&mut serializer).unwrap();
let bytes = to_vec(&()).unwrap();
let mut deserializer = de::Deserializer::from_slice(&bytes, usize::MAX).unwrap();
assert!(!(&mut deserializer).is_human_readable());
}
#[test]
fn unexpected_eof() {
let mut invalid_bytes = Vec::new();
format::write_header(&mut invalid_bytes, CURRENT_VERSION).unwrap();
format::write_atom_header(&mut invalid_bytes, format::Kind::Bytes, Some(10)).unwrap();
assert!(matches!(
from_slice::<Vec<u8>>(&invalid_bytes),
Err(Error::Eof)
));
}
#[test]
fn too_big_read() {
let mut invalid_bytes = Vec::new();
format::write_header(&mut invalid_bytes, CURRENT_VERSION).unwrap();
format::write_atom_header(&mut invalid_bytes, format::Kind::Bytes, Some(10)).unwrap();
assert!(matches!(
Config::default()
.allocation_budget(9)
.deserialize::<Vec<u8>>(&invalid_bytes),
Err(Error::TooManyBytesRead)
));
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Flatten {
#[serde(flatten)]
structure: Flattened,
#[serde(flatten)]
enumeration: EnumVariants,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Flattened {
field: String,
}
#[test]
fn test_flatten() {
test_serialization(
&Flatten {
structure: Flattened {
field: String::from("flat"),
},
enumeration: EnumVariants::Struct { arg: 1 },
},
None,
);
}
}