mirror of
https://github.com/serde-rs/serde.git
synced 2025-09-27 13:00:49 +00:00
Implement Ser+De for Saturating<T>
The serialization implementation is heavily inspired by the existing trait implentation for `std::num::Wrapping<T>`. The deserializing implementation maps input values that lie outside of the numerical range of the output type to the `MIN` or `MAX` value of the output type, depending on the sign of the input value. This behaviour follows to the `Saturating` semantics of the output type. fix #2708
This commit is contained in:
parent
5b24f88e73
commit
3d1b19ed90
@ -64,6 +64,12 @@ fn main() {
|
|||||||
if minor < 64 {
|
if minor < 64 {
|
||||||
println!("cargo:rustc-cfg=no_core_cstr");
|
println!("cargo:rustc-cfg=no_core_cstr");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Support for core::num::Saturating and std::num::Saturating stabilized in Rust 1.74
|
||||||
|
// https://blog.rust-lang.org/2023/11/16/Rust-1.74.0.html#stabilized-apis
|
||||||
|
if minor < 74 {
|
||||||
|
println!("cargo:rustc-cfg=no_core_num_saturating");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rustc_minor_version() -> Option<u32> {
|
fn rustc_minor_version() -> Option<u32> {
|
||||||
|
@ -387,6 +387,73 @@ impl_deserialize_num! {
|
|||||||
num_as_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64);
|
num_as_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(no_core_num_saturating))]
|
||||||
|
macro_rules! visit_saturating {
|
||||||
|
($primitive:ident, $ty:ident : $visit:ident) => {
|
||||||
|
#[inline]
|
||||||
|
fn $visit<E>(self, v: $ty) -> Result<Saturating<$primitive>, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
|
let out: $primitive = core::convert::TryFrom::<$ty>::try_from(v).unwrap_or_else(|_| {
|
||||||
|
#[allow(unused_comparisons)]
|
||||||
|
if v < 0 {
|
||||||
|
// never true for unsigned values
|
||||||
|
$primitive::MIN
|
||||||
|
} else {
|
||||||
|
$primitive::MAX
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(Saturating(out))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_deserialize_saturating_num {
|
||||||
|
($primitive:ident, $deserialize:ident ) => {
|
||||||
|
#[cfg(not(no_core_num_saturating))]
|
||||||
|
impl<'de> Deserialize<'de> for Saturating<$primitive> {
|
||||||
|
#[inline]
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct SaturatingVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for SaturatingVisitor {
|
||||||
|
type Value = Saturating<$primitive>;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("An integer with support for saturating semantics")
|
||||||
|
}
|
||||||
|
|
||||||
|
visit_saturating!($primitive, u8:visit_u8);
|
||||||
|
visit_saturating!($primitive, u16:visit_u16);
|
||||||
|
visit_saturating!($primitive, u32:visit_u32);
|
||||||
|
visit_saturating!($primitive, u64:visit_u64);
|
||||||
|
visit_saturating!($primitive, i8:visit_i8);
|
||||||
|
visit_saturating!($primitive, i16:visit_i16);
|
||||||
|
visit_saturating!($primitive, i32:visit_i32);
|
||||||
|
visit_saturating!($primitive, i64:visit_i64);
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.$deserialize(SaturatingVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_deserialize_saturating_num!(u8, deserialize_u8);
|
||||||
|
impl_deserialize_saturating_num!(u16, deserialize_u16);
|
||||||
|
impl_deserialize_saturating_num!(u32, deserialize_u32);
|
||||||
|
impl_deserialize_saturating_num!(u64, deserialize_u64);
|
||||||
|
impl_deserialize_saturating_num!(usize, deserialize_u64);
|
||||||
|
impl_deserialize_saturating_num!(i8, deserialize_i8);
|
||||||
|
impl_deserialize_saturating_num!(i16, deserialize_i16);
|
||||||
|
impl_deserialize_saturating_num!(i32, deserialize_i32);
|
||||||
|
impl_deserialize_saturating_num!(i64, deserialize_i64);
|
||||||
|
impl_deserialize_saturating_num!(isize, deserialize_i64);
|
||||||
|
|
||||||
macro_rules! num_128 {
|
macro_rules! num_128 {
|
||||||
($ty:ident : $visit:ident) => {
|
($ty:ident : $visit:ident) => {
|
||||||
fn $visit<E>(self, v: $ty) -> Result<Self::Value, E>
|
fn $visit<E>(self, v: $ty) -> Result<Self::Value, E>
|
||||||
|
@ -274,6 +274,9 @@ mod lib {
|
|||||||
pub use std::sync::atomic::{AtomicI64, AtomicU64};
|
pub use std::sync::atomic::{AtomicI64, AtomicU64};
|
||||||
#[cfg(all(feature = "std", not(no_target_has_atomic), target_has_atomic = "ptr"))]
|
#[cfg(all(feature = "std", not(no_target_has_atomic), target_has_atomic = "ptr"))]
|
||||||
pub use std::sync::atomic::{AtomicIsize, AtomicUsize};
|
pub use std::sync::atomic::{AtomicIsize, AtomicUsize};
|
||||||
|
|
||||||
|
#[cfg(not(no_core_num_saturating))]
|
||||||
|
pub use self::core::num::Saturating;
|
||||||
}
|
}
|
||||||
|
|
||||||
// None of this crate's error handling needs the `From::from` error conversion
|
// None of this crate's error handling needs the `From::from` error conversion
|
||||||
|
@ -1026,6 +1026,20 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(no_core_num_saturating))]
|
||||||
|
impl<T> Serialize for Saturating<T>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
self.0.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> Serialize for Reverse<T>
|
impl<T> Serialize for Reverse<T>
|
||||||
where
|
where
|
||||||
T: Serialize,
|
T: Serialize,
|
||||||
|
@ -23,7 +23,7 @@ use std::iter;
|
|||||||
use std::net;
|
use std::net;
|
||||||
use std::num::{
|
use std::num::{
|
||||||
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
|
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
|
||||||
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
|
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Saturating, Wrapping,
|
||||||
};
|
};
|
||||||
use std::ops::Bound;
|
use std::ops::Bound;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -2065,6 +2065,43 @@ fn test_wrapping() {
|
|||||||
test(Wrapping(1usize), &[Token::U64(1)]);
|
test(Wrapping(1usize), &[Token::U64(1)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_saturating() {
|
||||||
|
test(Saturating(1usize), &[Token::U32(1)]);
|
||||||
|
test(Saturating(1usize), &[Token::U64(1)]);
|
||||||
|
test(Saturating(0u8), &[Token::I8(0)]);
|
||||||
|
test(Saturating(0u16), &[Token::I16(0)]);
|
||||||
|
|
||||||
|
// saturate input values at the minimum or maximum value
|
||||||
|
test(Saturating(u8::MAX), &[Token::U16(u16::MAX)]);
|
||||||
|
test(Saturating(u8::MAX), &[Token::U16(u8::MAX as u16 + 1)]);
|
||||||
|
test(Saturating(u16::MAX), &[Token::U32(u32::MAX)]);
|
||||||
|
test(Saturating(u32::MAX), &[Token::U64(u64::MAX)]);
|
||||||
|
test(Saturating(u8::MIN), &[Token::I8(i8::MIN)]);
|
||||||
|
test(Saturating(u16::MIN), &[Token::I16(i16::MIN)]);
|
||||||
|
test(Saturating(u32::MIN), &[Token::I32(i32::MIN)]);
|
||||||
|
test(Saturating(i8::MIN), &[Token::I16(i16::MIN)]);
|
||||||
|
test(Saturating(i16::MIN), &[Token::I32(i32::MIN)]);
|
||||||
|
test(Saturating(i32::MIN), &[Token::I64(i64::MIN)]);
|
||||||
|
|
||||||
|
test(Saturating(u8::MIN), &[Token::I8(-1)]);
|
||||||
|
test(Saturating(u16::MIN), &[Token::I16(-1)]);
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
{
|
||||||
|
test(Saturating(usize::MIN), &[Token::U64(u64::MIN)]);
|
||||||
|
test(Saturating(usize::MAX), &[Token::U64(u64::MAX)]);
|
||||||
|
test(Saturating(isize::MIN), &[Token::I64(i64::MIN)]);
|
||||||
|
test(Saturating(isize::MAX), &[Token::I64(i64::MAX)]);
|
||||||
|
test(Saturating(0usize), &[Token::I64(i64::MIN)]);
|
||||||
|
|
||||||
|
test(
|
||||||
|
Saturating(9_223_372_036_854_775_807usize),
|
||||||
|
&[Token::I64(i64::MAX)],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rc_dst() {
|
fn test_rc_dst() {
|
||||||
test(Rc::<str>::from("s"), &[Token::Str("s")]);
|
test(Rc::<str>::from("s"), &[Token::Str("s")]);
|
||||||
|
@ -8,7 +8,7 @@ use std::cell::RefCell;
|
|||||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::net;
|
use std::net;
|
||||||
use std::num::Wrapping;
|
use std::num::{Saturating, Wrapping};
|
||||||
use std::ops::Bound;
|
use std::ops::Bound;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::{Rc, Weak as RcWeak};
|
use std::rc::{Rc, Weak as RcWeak};
|
||||||
@ -624,6 +624,11 @@ fn test_wrapping() {
|
|||||||
assert_ser_tokens(&Wrapping(1usize), &[Token::U64(1)]);
|
assert_ser_tokens(&Wrapping(1usize), &[Token::U64(1)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_saturating() {
|
||||||
|
assert_ser_tokens(&Saturating(1usize), &[Token::U64(1)]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rc_dst() {
|
fn test_rc_dst() {
|
||||||
assert_ser_tokens(&Rc::<str>::from("s"), &[Token::Str("s")]);
|
assert_ser_tokens(&Rc::<str>::from("s"), &[Token::Str("s")]);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user