use std::mem;
use std::error::Error;
use byteorder::{ByteOrder, LittleEndian};
use serde_json::value::{Value, Number};
use super::Result as EncodingResult;
use super::Error as EncodingError;
use encoding::{CheckedOffset, Field, Offset};
use encoding::serialize::WriteBufferWrapper;
use encoding::serialize::json::{ExonumJson, ExonumJsonDeserialize};
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct F32 {
value: f32,
}
impl F32 {
pub fn new(value: f32) -> Self {
Self::try_from(value).expect("Unexpected non-finite value")
}
pub fn try_from(value: f32) -> Option<Self> {
if value.is_normal() || (value == 0.0 && value.signum() == 1.0) {
Some(Self { value })
} else {
None
}
}
pub fn get(&self) -> f32 {
self.value
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct F64 {
value: f64,
}
impl F64 {
pub fn new(value: f64) -> Self {
Self::try_from(value).expect("Unexpected non-finite value")
}
pub fn try_from(value: f64) -> Option<Self> {
if value.is_normal() || (value == 0.0 && value.signum() == 1.0) {
Some(Self { value })
} else {
None
}
}
pub fn get(&self) -> f64 {
self.value
}
}
impl<'a> Field<'a> for F32 {
fn field_size() -> Offset {
mem::size_of::<Self>() as Offset
}
unsafe fn read(buffer: &'a [u8], from: Offset, to: Offset) -> Self {
Self::new(LittleEndian::read_f32(&buffer[from as usize..to as usize]))
}
fn write(&self, buffer: &mut Vec<u8>, from: Offset, to: Offset) {
LittleEndian::write_f32(&mut buffer[from as usize..to as usize], self.get());
}
fn check(
buffer: &'a [u8],
from: CheckedOffset,
to: CheckedOffset,
latest_segment: CheckedOffset,
) -> EncodingResult {
debug_assert_eq!((to - from)?.unchecked_offset(), Self::field_size());
let from = from.unchecked_offset();
let to = to.unchecked_offset();
let value = LittleEndian::read_f32(&buffer[from as usize..to as usize]);
match Self::try_from(value) {
Some(_) => Ok(latest_segment),
None => Err(EncodingError::UnsupportedFloat {
position: from,
value: f64::from(value),
}),
}
}
}
impl<'a> Field<'a> for F64 {
fn field_size() -> Offset {
mem::size_of::<Self>() as Offset
}
unsafe fn read(buffer: &'a [u8], from: Offset, to: Offset) -> Self {
Self::new(LittleEndian::read_f64(&buffer[from as usize..to as usize]))
}
fn write(&self, buffer: &mut Vec<u8>, from: Offset, to: Offset) {
LittleEndian::write_f64(&mut buffer[from as usize..to as usize], self.get());
}
fn check(
buffer: &'a [u8],
from: CheckedOffset,
to: CheckedOffset,
latest_segment: CheckedOffset,
) -> EncodingResult {
debug_assert_eq!((to - from)?.unchecked_offset(), Self::field_size());
let from = from.unchecked_offset();
let to = to.unchecked_offset();
let value = LittleEndian::read_f64(&buffer[from as usize..to as usize]);
match Self::try_from(value) {
Some(_) => Ok(latest_segment),
None => Err(EncodingError::UnsupportedFloat {
position: from,
value,
}),
}
}
}
impl ExonumJson for F32 {
fn deserialize_field<B: WriteBufferWrapper>(
value: &Value,
buffer: &mut B,
from: Offset,
to: Offset,
) -> Result<(), Box<Error>> {
let number = value.as_f64().ok_or("Can't cast json as float")?;
buffer.write(
from,
to,
Self::try_from(number as f32).ok_or(
"Invalid float value in json",
)?,
);
Ok(())
}
fn serialize_field(&self) -> Result<Value, Box<Error + Send + Sync>> {
Ok(Value::Number(
Number::from_f64(f64::from(self.get())).ok_or(
"Can't cast float as json",
)?,
))
}
}
impl ExonumJsonDeserialize for F32 {
fn deserialize(value: &Value) -> Result<Self, Box<Error>> {
let number = value.as_f64().ok_or("Can't cast json as float")?;
Ok(Self::try_from(number as f32).ok_or(
"Invalid float value in json",
)?)
}
}
impl ExonumJson for F64 {
fn deserialize_field<B: WriteBufferWrapper>(
value: &Value,
buffer: &mut B,
from: Offset,
to: Offset,
) -> Result<(), Box<Error>> {
let number = value.as_f64().ok_or("Can't cast json as float")?;
buffer.write(
from,
to,
Self::try_from(number).ok_or("Invalid float value in json")?,
);
Ok(())
}
fn serialize_field(&self) -> Result<Value, Box<Error + Send + Sync>> {
Ok(Value::Number(Number::from_f64(self.get()).ok_or(
"Can't cast float as json",
)?))
}
}
impl ExonumJsonDeserialize for F64 {
fn deserialize(value: &Value) -> Result<Self, Box<Error>> {
let number = value.as_f64().ok_or("Can't cast json as float")?;
Ok(Self::try_from(number).ok_or("Invalid float value in json")?)
}
}
#[cfg(test)]
mod tests {
use super::{F32, F64};
use std::num::FpCategory;
use std::{f32, f64};
use std::panic;
use encoding::fields::Field;
use byteorder::{LittleEndian, ByteOrder};
use encoding::Offset;
fn validate_constructor<T, V, C: Fn(V) -> T>(
constructor: C,
value: V,
buffer: &[u8],
header_size: Offset,
) -> bool
where
C: panic::RefUnwindSafe,
V: panic::UnwindSafe,
T: for<'r> Field<'r> + PartialEq + ::std::fmt::Debug,
{
let constructor_result = panic::catch_unwind(|| constructor(value));
let check_result =
<T as Field>::check(&buffer, 0.into(), header_size.into(), header_size.into());
if constructor_result.is_err() && check_result.is_err() {
return false;
} else if constructor_result.is_ok() && check_result.is_ok() {
let constructed = constructor_result.unwrap();
let read = unsafe { <T as Field>::read(&buffer, 0, header_size) };
assert_eq!(constructed, read);
return true;
} else {
panic!("{:?} != {:?}", constructor_result, check_result);
}
}
#[test]
fn test_f32_encoding() {
let sub: f32 = 1.1754942e-38;
assert_eq!(sub.classify(), FpCategory::Subnormal);
let valid_data = vec![0f32, 3.14, -1.0, 1.0, f32::MAX, f32::MIN];
let invalid_data = vec![-0.0f32, f32::INFINITY, f32::NEG_INFINITY, f32::NAN, sub];
let mut buf = vec![0; 4];
for value in valid_data {
LittleEndian::write_f32(&mut buf, value);
assert!(validate_constructor(|v| F32::new(v), value, &buf, 4));
}
for value in invalid_data {
LittleEndian::write_f32(&mut buf, value);
assert!(!validate_constructor(|v| F32::new(v), value, &buf, 4));
}
}
#[test]
fn test_f64_encoding() {
let sub: f64 = 1.1754942e-315;
assert_eq!(sub.classify(), FpCategory::Subnormal);
let valid_data = vec![0f64, 3.14, -1.0, 1.0, f64::MAX, f64::MIN];
let invalid_data = vec![-0.0f64, f64::INFINITY, f64::NEG_INFINITY, f64::NAN, sub];
let mut buf = vec![0; 8];
for value in valid_data {
LittleEndian::write_f64(&mut buf, value);
assert!(validate_constructor(|v| F64::new(v), value, &buf, 8));
}
for value in invalid_data {
LittleEndian::write_f64(&mut buf, value);
assert!(!validate_constructor(|v| F64::new(v), value, &buf, 8));
}
}
#[test]
#[allow(dead_code)]
fn test_f32_struct() {
encoding_struct!(
struct Msg {
single_float: F32,
vec: Vec<F32>,
}
);
let test_vec = vec![F32::new(0.0), F32::new(3.14), F32::new(5.82)];
let msg = Msg::new(F32::new(0.0), test_vec.clone());
assert_eq!(msg.single_float().get(), 0.0);
assert_eq!(msg.vec(), test_vec);
}
#[test]
#[allow(dead_code)]
fn test_f64_struct() {
encoding_struct!(
struct Msg {
single_float: F64,
vec: Vec<F64>,
}
);
let test_vec = vec![F64::new(0.0), F64::new(3.14), F64::new(5.82)];
let msg = Msg::new(F64::new(0.0), test_vec.clone());
assert_eq!(msg.single_float().get(), 0.0);
assert_eq!(msg.vec(), test_vec);
}
}