Coverage Report

Created: 2026-08-14 08:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-short-0.3.5/src/lib.rs
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#![no_std]
6
7
extern crate dtoa;
8
9
use core::fmt::Write;
10
use core::{fmt, str};
11
12
/// Format the given `value` into `dest` and return the notation it uses.
13
#[inline]
14
0
pub fn write<W: Write, V: Floating>(dest: &mut W, value: V) -> DtoaResult {
15
0
    Floating::write(value, dest)
16
0
}
Unexecuted instantiation: dtoa_short::write::<alloc::string::String, f32>
Unexecuted instantiation: dtoa_short::write::<_, _>
17
18
/// Form of the formatted floating-point number.
19
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
20
pub struct Notation {
21
    /// Whether it contains a decimal point.
22
    pub decimal_point: bool,
23
    /// Whether it uses E-notation.
24
    pub scientific: bool,
25
}
26
27
impl Notation {
28
0
    fn integer() -> Self {
29
0
        Notation {
30
0
            decimal_point: false,
31
0
            scientific: false,
32
0
        }
33
0
    }
34
}
35
36
/// Result of formatting the number.
37
pub type DtoaResult = Result<Notation, fmt::Error>;
38
39
pub trait Floating : dtoa::Float {
40
    fn write<W: Write>(self, dest: &mut W) -> DtoaResult;
41
}
42
43
impl Floating for f32 {
44
0
    fn write<W: Write>(self, dest: &mut W) -> DtoaResult {
45
0
        write_with_prec(dest, self, 6)
46
0
    }
Unexecuted instantiation: <f32 as dtoa_short::Floating>::write::<alloc::string::String>
Unexecuted instantiation: <f32 as dtoa_short::Floating>::write::<_>
47
}
48
49
impl Floating for f64 {
50
0
    fn write<W: Write>(self, dest: &mut W) -> DtoaResult {
51
0
        write_with_prec(dest, self, 15)
52
0
    }
53
}
54
55
0
fn write_with_prec<W, V>(dest: &mut W, value: V, prec: usize) -> DtoaResult
56
0
where
57
0
    W: Write,
58
0
    V: dtoa::Float
59
{
60
0
    let mut buf = dtoa::Buffer::new();
61
0
    let str = buf.format_finite(value);
62
63
    const SCRATCH_LEN: usize = core::mem::size_of::<dtoa::Buffer>() + 1;
64
0
    let mut scratch = [b'\0'; SCRATCH_LEN];
65
0
    debug_assert!(str.len() < SCRATCH_LEN);
66
0
    unsafe {
67
0
        core::ptr::copy_nonoverlapping(str.as_bytes().as_ptr(), scratch.as_mut_ptr().offset(1), str.len());
68
0
    }
69
0
    let (result, notation) = restrict_prec(&mut scratch[0..str.len() + 1], prec);
70
0
    dest.write_str(if cfg!(debug_assertions) {
71
0
        str::from_utf8(result).unwrap()
72
    } else {
73
        // safety: dtoa only generates ascii.
74
0
        unsafe { str::from_utf8_unchecked(result) }
75
0
    })?;
76
0
    Ok(notation)
77
0
}
Unexecuted instantiation: dtoa_short::write_with_prec::<alloc::string::String, f32>
Unexecuted instantiation: dtoa_short::write_with_prec::<_, _>
78
79
0
fn restrict_prec(buf: &mut [u8], prec: usize) -> (&[u8], Notation) {
80
0
    let len = buf.len();
81
    // Put a leading zero to capture any carry.
82
0
    debug_assert!(buf[0] == b'\0', "Caller must prepare an empty byte for us");
83
0
    buf[0] = b'0';
84
    // Remove the sign for now. We will put it back at the end.
85
0
    let sign = match buf[1] {
86
0
        s @ b'+' | s @ b'-' => {
87
0
            buf[1] = b'0';
88
0
            Some(s)
89
        }
90
0
        _ => None,
91
    };
92
    // Locate dot, exponent, and the first significant digit.
93
0
    let mut pos_dot = None;
94
0
    let mut pos_exp = None;
95
0
    let mut prec_start = None;
96
0
    for i in 1..len {
97
0
        if buf[i] == b'.' {
98
0
            debug_assert!(pos_dot.is_none());
99
0
            pos_dot = Some(i);
100
0
        } else if buf[i] == b'e' {
101
0
            pos_exp = Some(i);
102
            // We don't change exponent part, so stop here.
103
0
            break;
104
0
        } else if prec_start.is_none() && buf[i] != b'0' {
105
0
            debug_assert!(buf[i] >= b'1' && buf[i] <= b'9');
106
0
            prec_start = Some(i);
107
0
        }
108
    }
109
0
    let prec_start = match prec_start {
110
0
        Some(i) => i,
111
        // If there is no non-zero digit at all, it is just zero.
112
0
        None => return (&buf[0..1], Notation::integer()),
113
    };
114
    // Coefficient part ends at 'e' or the length.
115
0
    let coeff_end = pos_exp.unwrap_or(len);
116
    // Decimal dot is effectively at the end of coefficient part if no
117
    // dot presents before that.
118
0
    let pos_dot = pos_dot.unwrap_or(coeff_end);
119
    // Find the end position of the number within the given precision.
120
0
    let prec_end = {
121
0
        let end = prec_start + prec;
122
0
        if pos_dot > prec_start && pos_dot <= end {
123
0
            end + 1
124
        } else {
125
0
            end
126
        }
127
    };
128
0
    let mut new_coeff_end = coeff_end;
129
0
    if prec_end < coeff_end {
130
        // Round to the given precision.
131
0
        let next_char = buf[prec_end];
132
0
        new_coeff_end = prec_end;
133
0
        if next_char >= b'5' {
134
0
            for i in (0..prec_end).rev() {
135
0
                if buf[i] == b'.' {
136
0
                    continue;
137
0
                }
138
0
                if buf[i] != b'9' {
139
0
                    buf[i] += 1;
140
0
                    new_coeff_end = i + 1;
141
0
                    break;
142
0
                }
143
0
                buf[i] = b'0';
144
            }
145
0
        }
146
0
    }
147
0
    if new_coeff_end < pos_dot {
148
        // If the precision isn't enough to reach the dot, set all digits
149
        // in-between to zero and keep the number until the dot.
150
0
        for i in new_coeff_end..pos_dot {
151
0
            buf[i] = b'0';
152
0
        }
153
0
        new_coeff_end = pos_dot;
154
    } else {
155
        // Strip any trailing zeros.
156
0
        for i in (0..new_coeff_end).rev() {
157
0
            if buf[i] != b'0' {
158
0
                if buf[i] == b'.' {
159
0
                    new_coeff_end = i;
160
0
                }
161
0
                break;
162
0
            }
163
0
            new_coeff_end = i;
164
        }
165
    }
166
    // Move exponent part if necessary.
167
0
    let real_end = if let Some(pos_exp) = pos_exp {
168
0
        let exp_len = len - pos_exp;
169
0
        if new_coeff_end != pos_exp {
170
0
            for i in 0..exp_len {
171
0
                buf[new_coeff_end + i] = buf[pos_exp + i];
172
0
            }
173
0
        }
174
0
        new_coeff_end + exp_len
175
    } else {
176
0
        new_coeff_end
177
    };
178
    // Add back the sign and strip the leading zero.
179
0
    let result = if let Some(sign) = sign {
180
0
        if buf[1] == b'0' && buf[2] != b'.' {
181
0
            buf[1] = sign;
182
0
            &buf[1..real_end]
183
        } else {
184
0
            debug_assert!(buf[0] == b'0');
185
0
            buf[0] = sign;
186
0
            &buf[0..real_end]
187
        }
188
    } else {
189
0
        if buf[0] == b'0' && buf[1] != b'.' {
190
0
            &buf[1..real_end]
191
        } else {
192
0
            &buf[0..real_end]
193
        }
194
    };
195
    // Generate the notation info.
196
0
    let notation = Notation {
197
0
        decimal_point: pos_dot < new_coeff_end,
198
0
        scientific: pos_exp.is_some(),
199
0
    };
200
0
    (result, notation)
201
0
}