use serde::ser::{
self, Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant,
SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, Serializer,
};
use std::cell::Cell;
pub struct Noticed {
depth: Cell<usize>,
overflowed: Cell<bool>,
non_finite: Cell<bool>,
enum_variant: Cell<bool>,
inside_a_some: Cell<bool>,
collapsed_option: Cell<bool>,
past_i64: Cell<bool>,
wide_integer: Cell<bool>,
past_64_bits: Cell<bool>,
limit: usize,
}
impl Noticed {
pub fn new(limit: usize) -> Self {
Self {
depth: Cell::new(0),
overflowed: Cell::new(false),
non_finite: Cell::new(false),
enum_variant: Cell::new(false),
inside_a_some: Cell::new(false),
collapsed_option: Cell::new(false),
past_i64: Cell::new(false),
wide_integer: Cell::new(false),
past_64_bits: Cell::new(false),
limit,
}
}
pub fn unlimited() -> Self {
Self::new(usize::MAX)
}
pub fn wrap<S>(&self, serializer: S) -> Counting<'_, S> {
Counting {
inner: serializer,
depth: self,
}
}
pub fn count<'v, T>(&self, value: &'v T) -> Counted<'v, '_, T>
where
T: Serialize + ?Sized,
{
Counted { value, depth: self }
}
pub fn overflowed(&self) -> bool {
self.overflowed.get()
}
pub fn saw_a_non_finite_float(&self) -> bool {
self.non_finite.get()
}
pub fn saw_an_enum(&self) -> bool {
self.enum_variant.get()
}
pub fn take_the_enum_from(&self, other: &Noticed) {
self.enum_variant.set(other.saw_an_enum());
}
pub fn saw_a_collapsing_option(&self) -> bool {
self.collapsed_option.get()
}
pub fn saw_an_integer_past_i64(&self) -> bool {
self.past_i64.get()
}
pub fn saw_a_128_bit_integer(&self) -> bool {
self.wide_integer.get()
}
pub fn saw_an_integer_past_64_bits(&self) -> bool {
self.past_64_bits.get()
}
fn left_the_some(&self) {
self.inside_a_some.set(false);
}
fn enter<E: ser::Error>(&self) -> Result<(), E> {
self.left_the_some();
let now = self.depth.get() + 1;
if now > self.limit {
self.overflowed.set(true);
return Err(E::custom(format_args!(
"the value nests deeper than {} levels",
self.limit
)));
}
self.depth.set(now);
Ok(())
}
fn leave(&self) {
self.depth.set(self.depth.get().saturating_sub(1));
}
}
pub struct Counting<'a, S> {
inner: S,
depth: &'a Noticed,
}
pub struct Counted<'v, 'd, T: ?Sized> {
value: &'v T,
depth: &'d Noticed,
}
impl<T> Serialize for Counted<'_, '_, T>
where
T: Serialize + ?Sized,
{
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.value.serialize(Counting {
inner: serializer,
depth: self.depth,
})
}
}
macro_rules! forward {
($($method:ident($($arg:ident: $ty:ty),*);)+) => {
$(fn $method(self $(, $arg: $ty)*) -> Result<S::Ok, S::Error> {
self.depth.left_the_some();
self.inner.$method($($arg),*)
})+
};
}
impl<'a, S: Serializer> Serializer for Counting<'a, S> {
type Ok = S::Ok;
type Error = S::Error;
type SerializeSeq = Counting<'a, S::SerializeSeq>;
type SerializeTuple = Counting<'a, S::SerializeTuple>;
type SerializeTupleStruct = Counting<'a, S::SerializeTupleStruct>;
type SerializeTupleVariant = Counting<'a, S::SerializeTupleVariant>;
type SerializeMap = Counting<'a, S::SerializeMap>;
type SerializeStruct = Counting<'a, S::SerializeStruct>;
type SerializeStructVariant = Counting<'a, S::SerializeStructVariant>;
forward! {
serialize_bool(v: bool);
serialize_i8(v: i8);
serialize_i16(v: i16);
serialize_i32(v: i32);
serialize_i64(v: i64);
serialize_u8(v: u8);
serialize_u16(v: u16);
serialize_u32(v: u32);
serialize_char(v: char);
serialize_str(v: &str);
serialize_bytes(v: &[u8]);
serialize_unit();
serialize_unit_struct(name: &'static str);
}
fn serialize_u64(self, v: u64) -> Result<S::Ok, S::Error> {
self.depth.left_the_some();
if v > i64::MAX as u64 {
self.depth.past_i64.set(true);
}
self.inner.serialize_u64(v)
}
fn serialize_u128(self, v: u128) -> Result<S::Ok, S::Error> {
self.depth.left_the_some();
self.depth.wide_integer.set(true);
if v > i64::MAX as u128 {
self.depth.past_i64.set(true);
}
if v > u64::MAX as u128 {
self.depth.past_64_bits.set(true);
}
self.inner.serialize_u128(v)
}
fn serialize_i128(self, v: i128) -> Result<S::Ok, S::Error> {
self.depth.left_the_some();
self.depth.wide_integer.set(true);
if v > i64::MAX as i128 || v < i64::MIN as i128 {
self.depth.past_i64.set(true);
}
if v > u64::MAX as i128 || v < i64::MIN as i128 {
self.depth.past_64_bits.set(true);
}
self.inner.serialize_i128(v)
}
fn serialize_none(self) -> Result<S::Ok, S::Error> {
if self.depth.inside_a_some.get() {
self.depth.collapsed_option.set(true);
}
self.depth.left_the_some();
self.inner.serialize_none()
}
fn serialize_unit_variant(
self,
name: &'static str,
index: u32,
variant: &'static str,
) -> Result<S::Ok, S::Error> {
self.depth.left_the_some();
self.depth.enum_variant.set(true);
self.inner.serialize_unit_variant(name, index, variant)
}
fn serialize_f32(self, v: f32) -> Result<S::Ok, S::Error> {
self.depth.left_the_some();
if !v.is_finite() {
self.depth.non_finite.set(true);
}
self.inner.serialize_f32(v)
}
fn serialize_f64(self, v: f64) -> Result<S::Ok, S::Error> {
self.depth.left_the_some();
if !v.is_finite() {
self.depth.non_finite.set(true);
}
self.inner.serialize_f64(v)
}
fn serialize_some<T>(self, value: &T) -> Result<S::Ok, S::Error>
where
T: Serialize + ?Sized,
{
let Counting { inner, depth } = self;
depth.inside_a_some.set(true);
let out = inner.serialize_some(&Counted { value, depth });
depth.left_the_some();
out
}
fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<S::Ok, S::Error>
where
T: Serialize + ?Sized,
{
let Counting { inner, depth } = self;
depth.left_the_some();
inner.serialize_newtype_struct(name, &Counted { value, depth })
}
fn serialize_newtype_variant<T>(
self,
name: &'static str,
index: u32,
variant: &'static str,
value: &T,
) -> Result<S::Ok, S::Error>
where
T: Serialize + ?Sized,
{
let Counting { inner, depth } = self;
depth.left_the_some();
depth.enum_variant.set(true);
depth.enter()?;
let out = inner.serialize_newtype_variant(name, index, variant, &Counted { value, depth });
depth.leave();
out
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, S::Error> {
self.depth.enter()?;
Ok(Counting {
inner: self.inner.serialize_seq(len)?,
depth: self.depth,
})
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, S::Error> {
self.depth.enter()?;
Ok(Counting {
inner: self.inner.serialize_tuple(len)?,
depth: self.depth,
})
}
fn serialize_tuple_struct(
self,
name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, S::Error> {
self.depth.enter()?;
Ok(Counting {
inner: self.inner.serialize_tuple_struct(name, len)?,
depth: self.depth,
})
}
fn serialize_tuple_variant(
self,
name: &'static str,
index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, S::Error> {
self.depth.enum_variant.set(true);
self.depth.enter()?;
Ok(Counting {
inner: self
.inner
.serialize_tuple_variant(name, index, variant, len)?,
depth: self.depth,
})
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, S::Error> {
self.depth.enter()?;
Ok(Counting {
inner: self.inner.serialize_map(len)?,
depth: self.depth,
})
}
fn serialize_struct(
self,
name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, S::Error> {
self.depth.enter()?;
Ok(Counting {
inner: self.inner.serialize_struct(name, len)?,
depth: self.depth,
})
}
fn serialize_struct_variant(
self,
name: &'static str,
index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeStructVariant, S::Error> {
self.depth.enum_variant.set(true);
self.depth.enter()?;
Ok(Counting {
inner: self
.inner
.serialize_struct_variant(name, index, variant, len)?,
depth: self.depth,
})
}
fn is_human_readable(&self) -> bool {
self.inner.is_human_readable()
}
fn collect_str<T>(self, value: &T) -> Result<S::Ok, S::Error>
where
T: std::fmt::Display + ?Sized,
{
self.inner.collect_str(value)
}
}
macro_rules! compound {
($($trait_name:ident { $($method:ident($($arg:ident: $ty:ty),*)),+ $(,)? })+) => {
$(impl<S: $trait_name> $trait_name for Counting<'_, S> {
type Ok = S::Ok;
type Error = S::Error;
$(fn $method<T>(&mut self $(, $arg: $ty)*, value: &T) -> Result<(), S::Error>
where
T: Serialize + ?Sized,
{
let nested = Counted { value, depth: self.depth };
self.inner.$method($($arg,)* &nested)
})+
fn end(self) -> Result<S::Ok, S::Error> {
self.depth.leave();
self.inner.end()
}
})+
};
}
compound! {
SerializeSeq { serialize_element() }
SerializeTuple { serialize_element() }
SerializeTupleStruct { serialize_field() }
SerializeTupleVariant { serialize_field() }
SerializeStruct { serialize_field(key: &'static str) }
SerializeStructVariant { serialize_field(key: &'static str) }
}
impl<S: SerializeMap> SerializeMap for Counting<'_, S> {
type Ok = S::Ok;
type Error = S::Error;
fn serialize_key<T>(&mut self, key: &T) -> Result<(), S::Error>
where
T: Serialize + ?Sized,
{
let nested = Counted {
value: key,
depth: self.depth,
};
self.inner.serialize_key(&nested)
}
fn serialize_value<T>(&mut self, value: &T) -> Result<(), S::Error>
where
T: Serialize + ?Sized,
{
let nested = Counted {
value,
depth: self.depth,
};
self.inner.serialize_value(&nested)
}
fn end(self) -> Result<S::Ok, S::Error> {
self.depth.leave();
self.inner.end()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn past_i64<T: Serialize>(value: T) -> bool {
let seen = Noticed::unlimited();
let _ = serde_json::to_vec(&seen.count(&value));
seen.saw_an_integer_past_i64()
}
#[test]
fn an_integer_is_past_i64_only_beyond_either_end() {
assert!(!past_i64(42u64));
assert!(!past_i64(i64::MAX as u64));
assert!(past_i64(i64::MAX as u64 + 1));
assert!(!past_i64(42u128));
assert!(!past_i64(i64::MAX as u128));
assert!(past_i64(i64::MAX as u128 + 1));
assert!(!past_i64(-42i128));
assert!(!past_i64(i64::MAX as i128));
assert!(!past_i64(i64::MIN as i128));
assert!(past_i64(i64::MAX as i128 + 1));
assert!(past_i64(i64::MIN as i128 - 1));
}
fn noticed<T: Serialize>(value: T) -> Noticed {
let seen = Noticed::unlimited();
let _ = serde_json::to_vec(&seen.count(&value));
seen
}
#[test]
fn a_128_bit_integer_is_noticed_whatever_it_holds() {
assert!(!noticed(42u64).saw_a_128_bit_integer());
assert!(!noticed(u64::MAX).saw_a_128_bit_integer());
assert!(noticed(42u128).saw_a_128_bit_integer());
assert!(noticed(0i128).saw_a_128_bit_integer());
assert!(noticed(u128::MAX).saw_a_128_bit_integer());
}
#[test]
fn an_integer_is_past_64_bits_only_where_neither_i64_nor_u64_holds_it() {
assert!(!noticed(u64::MAX).saw_an_integer_past_64_bits());
assert!(!noticed(u64::MAX as u128).saw_an_integer_past_64_bits());
assert!(!noticed(u64::MAX as i128).saw_an_integer_past_64_bits());
assert!(!noticed(i64::MIN as i128).saw_an_integer_past_64_bits());
assert!(noticed(u64::MAX as u128 + 1).saw_an_integer_past_64_bits());
assert!(noticed(u64::MAX as i128 + 1).saw_an_integer_past_64_bits());
assert!(noticed(i64::MIN as i128 - 1).saw_an_integer_past_64_bits());
}
#[cfg(feature = "redb")]
#[test]
fn the_codec_is_asked_whether_it_writes_for_a_reader() {
let seen = Noticed::unlimited();
let mut json = serde_json::Serializer::new(Vec::new());
assert!(serde::Serializer::is_human_readable(&seen.wrap(&mut json)));
let mut msgpack = rmp_serde::Serializer::new(Vec::new());
assert!(!serde::Serializer::is_human_readable(
&seen.wrap(&mut msgpack)
));
}
}