lexical_write_float/
write.rs

1//! Shared trait and methods for writing floats.
2
3#![doc(hidden)]
4
5#[cfg(feature = "f16")]
6use lexical_util::bf16::bf16;
7#[cfg(feature = "f16")]
8use lexical_util::f16::f16;
9use lexical_util::format::NumberFormat;
10use lexical_util::{algorithm::copy_to_dst, constants::FormattedSize};
11use lexical_write_integer::write::WriteInteger;
12
13/// Select the back-end.
14#[cfg(not(feature = "compact"))]
15use crate::algorithm::write_float as write_float_decimal;
16#[cfg(feature = "power-of-two")]
17use crate::binary;
18#[cfg(feature = "compact")]
19use crate::compact::write_float as write_float_decimal;
20use crate::float::RawFloat;
21#[cfg(feature = "power-of-two")]
22use crate::hex;
23use crate::options::Options;
24#[cfg(feature = "radix")]
25use crate::radix;
26
27/// Write an special string to the buffer.
28#[inline(always)]
29fn write_special(bytes: &mut [u8], special: Option<&[u8]>, error: &'static str) -> usize {
30    // The NaN string must be <= 50 characters, so this should never panic.
31    if let Some(special_str) = special {
32        debug_assert!(special_str.len() <= 50, "special_str.len() must be <= 50");
33        copy_to_dst(bytes, special_str)
34    } else {
35        // PANIC: the format does not support serializing that special.
36        panic!("{}", error);
37    }
38}
39
40/// Write an NaN string to the buffer.
41fn write_nan(bytes: &mut [u8], options: &Options, count: usize) -> usize {
42    count
43        + write_special(
44            bytes,
45            options.nan_string(),
46            "NaN explicitly disabled but asked to write NaN as string.",
47        )
48}
49
50/// Write an Inf string to the buffer.
51fn write_inf(bytes: &mut [u8], options: &Options, count: usize) -> usize {
52    count
53        + write_special(
54            bytes,
55            options.inf_string(),
56            "Inf explicitly disabled but asked to write Inf as string.",
57        )
58}
59
60/// Check if a buffer is sufficiently large.
61#[inline(always)]
62fn check_buffer<T, const FORMAT: u128>(len: usize, options: &Options) -> bool
63where
64    T: FormattedSize,
65{
66    let size = Options::buffer_size_const::<T, FORMAT>(options);
67    len >= size
68}
69
70/// Write float trait.
71pub trait WriteFloat: RawFloat + FormattedSize {
72    /// Forward float writing parameters and write the float.
73    ///
74    /// This abstracts away handling different optimizations and radices into
75    /// a single API.
76    ///
77    /// # Panics
78    ///
79    /// Panics if the number format is invalid, or if scientific notation
80    /// is used and the exponent base does not equal the mantissa radix
81    /// and the format is not a hexadecimal float. It also panics
82    /// if `options.nan_string` or `options.inf_string` is None and asked
83    /// to serialize a NaN or Inf value.
84    ///
85    /// [`FORMATTED_SIZE`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE
86    /// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL
87    #[cfg_attr(not(feature = "compact"), inline(always))]
88    fn write_float<const FORMAT: u128>(self, bytes: &mut [u8], options: &Options) -> usize
89    where
90        Self::Unsigned: FormattedSize + WriteInteger,
91    {
92        // Validate our format options.
93        assert!(check_buffer::<Self, { FORMAT }>(bytes.len(), options));
94        let format = NumberFormat::<FORMAT> {};
95        assert!(format.is_valid());
96        // Avoid any false assumptions for 128-bit floats.
97        assert!(Self::BITS <= 64);
98
99        #[cfg(feature = "power-of-two")]
100        {
101            // FIXME: I believe this incorrectly handles a few cases.
102            if format.radix() != format.exponent_base() {
103                assert!(matches!(
104                    (format.radix(), format.exponent_base()),
105                    (4, 2) | (8, 2) | (16, 2) | (32, 2) | (16, 4)
106                ));
107            }
108        }
109
110        let (float, count, bytes) = if self.needs_negative_sign() {
111            bytes[0] = b'-';
112            (-self, 1, &mut bytes[1..])
113        } else if cfg!(feature = "format") && format.required_mantissa_sign() {
114            bytes[0] = b'+';
115            (self, 1, &mut bytes[1..])
116        } else {
117            (self, 0, bytes)
118        };
119
120        // Handle special values.
121        if !self.is_special() {
122            #[cfg(all(feature = "power-of-two", not(feature = "radix")))]
123            {
124                let radix = format.radix();
125                let exponent_base = format.exponent_base();
126                count
127                    + if radix == 10 {
128                        write_float_decimal::<_, FORMAT>(float, bytes, options)
129                    } else if radix != exponent_base {
130                        hex::write_float::<_, FORMAT>(float, bytes, options)
131                    } else {
132                        binary::write_float::<_, FORMAT>(float, bytes, options)
133                    }
134            }
135
136            #[cfg(feature = "radix")]
137            {
138                let radix = format.radix();
139                let exponent_base = format.exponent_base();
140                count
141                    + if radix == 10 {
142                        write_float_decimal::<_, FORMAT>(float, bytes, options)
143                    } else if radix != exponent_base {
144                        hex::write_float::<_, FORMAT>(float, bytes, options)
145                    } else if matches!(radix, 2 | 4 | 8 | 16 | 32) {
146                        binary::write_float::<_, FORMAT>(float, bytes, options)
147                    } else {
148                        radix::write_float::<_, FORMAT>(float, bytes, options)
149                    }
150            }
151
152            #[cfg(not(feature = "power-of-two"))]
153            {
154                count + write_float_decimal::<_, FORMAT>(float, bytes, options)
155            }
156        } else if self.is_nan() {
157            write_nan(bytes, options, count)
158        } else {
159            write_inf(bytes, options, count)
160        }
161    }
162}
163
164macro_rules! write_float_impl {
165    ($($t:ty)*) => ($(
166        impl WriteFloat for $t {}
167    )*)
168}
169
170write_float_impl! { f32 f64 }
171
172#[cfg(feature = "f16")]
173macro_rules! write_float_as_f32 {
174    ($($t:ty)*) => ($(
175        impl WriteFloat for $t {
176            #[inline(always)]
177            fn write_float<const FORMAT: u128>(self, bytes: &mut [u8], options: &Options) -> usize
178            {
179                self.as_f32().write_float::<FORMAT>(bytes, options)
180            }
181        }
182    )*)
183}
184
185#[cfg(feature = "f16")]
186write_float_as_f32! { bf16 f16 }