Skip to main content

triblespace_core/inline/encodings/
f64.rs

1use crate::inline::Encodes;
2use crate::id::ExclusiveId;
3use crate::id::Id;
4use crate::id_hex;
5use crate::macros::entity;
6use crate::metadata;
7use crate::metadata::MetaDescribe;
8use crate::trible::Fragment;
9use crate::inline::IntoInline;
10use crate::inline::TryFromInline;
11use crate::inline::TryToInline;
12use crate::inline::Inline;
13use crate::inline::InlineEncoding;
14use serde_json::Number as JsonNumber;
15use std::convert::Infallible;
16use std::fmt;
17
18/// A inline encoding for an IEEE-754 double in little-endian byte order.
19pub struct F64;
20
21impl MetaDescribe for F64 {
22    fn describe() -> Fragment {
23        let id: Id = id_hex!("C80A60F4A6F2FBA5A8DB2531A923EC70");
24        #[allow(unused_mut)]
25        let mut tribles = entity! {
26            ExclusiveId::force_ref(&id) @
27                metadata::name: "f64",
28                metadata::description: "IEEE-754 double stored in the first 8 bytes (little-endian); remaining bytes are zero. This matches the standard host representation while preserving the 32-byte value width.\n\nUse for typical metrics, measurements, and calculations where floating-point rounding is acceptable. Choose F256 for higher precision or lossless JSON number import, and R256 for exact rational values.\n\nNaN and infinity can be represented; decide whether your application accepts them. If you need deterministic ordering or exact comparisons, prefer integer or rational schemas.",
29                metadata::tag: metadata::KIND_INLINE_ENCODING,
30        };
31
32        #[cfg(feature = "wasm")]
33        {
34            tribles += entity! { ExclusiveId::force_ref(&id) @
35                metadata::value_formatter: wasm_formatter::F64_WASM,
36            };
37        }
38        tribles
39    }
40}
41
42#[cfg(feature = "wasm")]
43mod wasm_formatter {
44    use core::fmt::Write;
45
46    use triblespace_core_macros::value_formatter;
47
48    #[value_formatter(const_wasm = F64_WASM)]
49    pub(crate) fn float64(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
50        let mut bytes = [0u8; 8];
51        bytes.copy_from_slice(&raw[..8]);
52        let value = f64::from_le_bytes(bytes);
53        write!(out, "{value}").map_err(|_| 1u32)?;
54        Ok(())
55    }
56}
57
58impl InlineEncoding for F64 {
59    type ValidationError = Infallible;
60    type Encoding = Self;
61}
62
63impl TryFromInline<'_, F64> for f64 {
64    type Error = Infallible;
65    fn try_from_inline(v: &Inline<F64>) -> Result<Self, Infallible> {
66        let mut bytes = [0u8; 8];
67        bytes.copy_from_slice(&v.raw[..8]);
68        Ok(f64::from_le_bytes(bytes))
69    }
70}
71
72impl Encodes<f64> for F64
73{
74    type Output = Inline<F64>;
75    fn encode(source: f64) -> Inline<F64> {
76        let mut raw = [0u8; 32];
77        raw[..8].copy_from_slice(&source.to_le_bytes());
78        Inline::new(raw)
79    }
80}
81
82/// Errors encountered when converting JSON numbers into [`F64`] values.
83#[derive(Debug, Clone, PartialEq)]
84pub enum JsonNumberToF64Error {
85    /// The numeric value could not be represented as an `f64` (non-finite or out of range).
86    Unrepresentable,
87}
88
89impl fmt::Display for JsonNumberToF64Error {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        match self {
92            JsonNumberToF64Error::Unrepresentable => {
93                write!(f, "number is too large to represent as f64")
94            }
95        }
96    }
97}
98
99impl std::error::Error for JsonNumberToF64Error {}
100
101impl TryToInline<F64> for JsonNumber {
102    type Error = JsonNumberToF64Error;
103
104    fn try_to_inline(self) -> Result<Inline<F64>, Self::Error> {
105        (&self).try_to_inline()
106    }
107}
108
109impl TryToInline<F64> for &JsonNumber {
110    type Error = JsonNumberToF64Error;
111
112    fn try_to_inline(self) -> Result<Inline<F64>, Self::Error> {
113        if let Some(value) = self.as_f64().filter(|v| v.is_finite()) {
114            return Ok(value.to_inline());
115        }
116        Err(JsonNumberToF64Error::Unrepresentable)
117    }
118}