triblespace_core/inline/encodings/
f64.rs1use 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
18pub 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#[derive(Debug, Clone, PartialEq)]
84pub enum JsonNumberToF64Error {
85 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}