core_json_traits/
float.rs

1use core::num::FpCategory;
2use crate::{Read, Stack, JsonError, Value, JsonDeserialize, JsonSerialize};
3
4impl JsonDeserialize for f64 {
5  fn deserialize<'read, 'parent, B: Read<'read>, S: Stack>(
6    value: Value<'read, 'parent, B, S>,
7  ) -> Result<Self, JsonError<'read, B, S>> {
8    value.to_number()?.f64().ok_or(JsonError::TypeError)
9  }
10}
11
12/// A JSON-compatible `f64`.
13///
14/// JSON does not support representing `NaN`, `inf`, but rather only well-defined values. This
15/// ensures the `f64` is representable within JSON. We additionally limit to normal `f64`s to
16/// achieve certain bounds.
17#[derive(Clone, Copy, Default, Debug)]
18pub struct JsonF64(f64);
19
20impl TryFrom<f64> for JsonF64 {
21  type Error = FpCategory;
22  fn try_from(value: f64) -> Result<Self, Self::Error> {
23    let class = value.classify();
24    match class {
25      FpCategory::Nan | FpCategory::Infinite => Err(class)?,
26      FpCategory::Zero | FpCategory::Normal | FpCategory::Subnormal => {}
27    }
28    Ok(Self(value))
29  }
30}
31
32impl From<JsonF64> for f64 {
33  fn from(value: JsonF64) -> f64 {
34    value.0
35  }
36}
37
38impl JsonDeserialize for JsonF64 {
39  fn deserialize<'read, 'parent, B: Read<'read>, S: Stack>(
40    value: Value<'read, 'parent, B, S>,
41  ) -> Result<Self, JsonError<'read, B, S>> {
42    JsonF64::try_from(f64::deserialize(value)?).map_err(|_| JsonError::TypeError)
43  }
44}
45
46#[cfg(not(feature = "ryu"))]
47mod serialize {
48  use core::fmt::Write;
49  use crate::NumberSink;
50  use super::*;
51
52  impl JsonSerialize for JsonF64 {
53    /// This will only serialize the `f64::DIGITS` most significant digits.
54    fn serialize(&self) -> impl Iterator<Item = char> {
55      let mut sink = NumberSink::new();
56      write!(&mut sink, "{}", self.0).expect("infallible `NumberSink` raised an error");
57      let (buf, len) = sink.imprecise_str().expect("`NumberSink` couldn't sink a `f64` from Rust");
58      // Safe as all of the written-to values will be written-to with ASCII characters
59      buf.into_iter().take(len).map(|b| b as char)
60    }
61  }
62}
63
64#[cfg(feature = "ryu")]
65mod serialize {
66  use super::*;
67
68  impl JsonSerialize for JsonF64 {
69    fn serialize(&self) -> impl Iterator<Item = char> {
70      let mut buffer = ryu::Buffer::new();
71      // Safe as `JsonF64` ensures this isn't `NaN`, `inf`
72      let result = buffer.format_finite(self.0).as_bytes();
73      /*
74        `ryu` yields us a string slice when we need an owned value to iterate, unfortunately, so
75        we copy the yielded string (a reference to the Buffer) into our own buffer (of equivalent
76        size)
77      */
78      let mut owned = [0; core::mem::size_of::<ryu::Buffer>()];
79      owned[.. result.len()].copy_from_slice(result);
80      // Safe to cast to char as `ryu` yields human-readable ASCII characters
81      owned.into_iter().take(result.len()).map(|byte| byte as char)
82    }
83  }
84}