diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/dtoa-short/src | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/dtoa-short/src')
-rw-r--r-- | third_party/rust/dtoa-short/src/lib.rs | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/third_party/rust/dtoa-short/src/lib.rs b/third_party/rust/dtoa-short/src/lib.rs new file mode 100644 index 0000000000..c5c23eeade --- /dev/null +++ b/third_party/rust/dtoa-short/src/lib.rs @@ -0,0 +1,197 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +extern crate dtoa; + +use std::fmt::Write; +use std::{fmt, str}; + +/// Format the given `value` into `dest` and return the notation it uses. +#[inline] +pub fn write<W: Write, V: Floating>(dest: &mut W, value: V) -> DtoaResult { + Floating::write(value, dest) +} + +/// Form of the formatted floating-point number. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Notation { + /// Whether it contains a decimal point. + pub decimal_point: bool, + /// Whether it uses E-notation. + pub scientific: bool, +} + +impl Notation { + fn integer() -> Self { + Notation { + decimal_point: false, + scientific: false, + } + } +} + +/// Result of formatting the number. +pub type DtoaResult = Result<Notation, fmt::Error>; + +pub trait Floating : dtoa::Floating { + fn write<W: Write>(self, dest: &mut W) -> DtoaResult; +} + +impl Floating for f32 { + fn write<W: Write>(self, dest: &mut W) -> DtoaResult { + write_with_prec(dest, self, 6) + } +} + +impl Floating for f64 { + fn write<W: Write>(self, dest: &mut W) -> DtoaResult { + write_with_prec(dest, self, 15) + } +} + +// dtoa's buffer is 24 bytes, so we use the same length here. We may +// need to update if dtoa changes its number in the future. See +// https://github.com/dtolnay/dtoa/blob/ +// 584674a70af74521ce40350dba776ea67cfcbaa7/src/dtoa.rs#L465 +const BUFFER_SIZE: usize = 24; + +fn write_with_prec<W, V>(dest: &mut W, value: V, prec: usize) + -> DtoaResult where W: Write, V: dtoa::Floating +{ + let mut buf = [b'\0'; BUFFER_SIZE + 8]; + let len = dtoa::write(&mut buf[1..], value).map_err(|_| fmt::Error)?; + let (result, notation) = restrict_prec(&mut buf[0..len + 1], prec); + dest.write_str(if cfg!(debug_assertions) { + str::from_utf8(result).unwrap() + } else { + // safety: dtoa only generates ascii. + unsafe { str::from_utf8_unchecked(result) } + })?; + Ok(notation) +} + +fn restrict_prec(buf: &mut [u8], prec: usize) -> (&[u8], Notation) { + let len = buf.len(); + debug_assert!(len <= BUFFER_SIZE + 1, "dtoa may have changed its buffer size"); + // Put a leading zero to capture any carry. + debug_assert!(buf[0] == b'\0', "Caller must prepare an empty byte for us"); + buf[0] = b'0'; + // Remove the sign for now. We will put it back at the end. + let sign = match buf[1] { + s @ b'+' | s @ b'-' => { + buf[1] = b'0'; + Some(s) + } + _ => None, + }; + // Locate dot, exponent, and the first significant digit. + let mut pos_dot = None; + let mut pos_exp = None; + let mut prec_start = None; + for i in 1..len { + if buf[i] == b'.' { + debug_assert!(pos_dot.is_none()); + pos_dot = Some(i); + } else if buf[i] == b'e' { + pos_exp = Some(i); + // We don't change exponent part, so stop here. + break; + } else if prec_start.is_none() && buf[i] != b'0' { + debug_assert!(buf[i] >= b'1' && buf[i] <= b'9'); + prec_start = Some(i); + } + } + let prec_start = match prec_start { + Some(i) => i, + // If there is no non-zero digit at all, it is just zero. + None => return (&buf[0..1], Notation::integer()), + }; + // Coefficient part ends at 'e' or the length. + let coeff_end = pos_exp.unwrap_or(len); + // Decimal dot is effectively at the end of coefficient part if no + // dot presents before that. + let pos_dot = pos_dot.unwrap_or(coeff_end); + // Find the end position of the number within the given precision. + let prec_end = { + let end = prec_start + prec; + if pos_dot > prec_start && pos_dot <= end { + end + 1 + } else { + end + } + }; + let mut new_coeff_end = coeff_end; + if prec_end < coeff_end { + // Round to the given precision. + let next_char = buf[prec_end]; + new_coeff_end = prec_end; + if next_char >= b'5' { + for i in (0..prec_end).rev() { + if buf[i] == b'.' { + continue; + } + if buf[i] != b'9' { + buf[i] += 1; + new_coeff_end = i + 1; + break; + } + buf[i] = b'0'; + } + } + } + if new_coeff_end < pos_dot { + // If the precision isn't enough to reach the dot, set all digits + // in-between to zero and keep the number until the dot. + for i in new_coeff_end..pos_dot { + buf[i] = b'0'; + } + new_coeff_end = pos_dot; + } else { + // Strip any trailing zeros. + for i in (0..new_coeff_end).rev() { + if buf[i] != b'0' { + if buf[i] == b'.' { + new_coeff_end = i; + } + break; + } + new_coeff_end = i; + } + } + // Move exponent part if necessary. + let real_end = if let Some(pos_exp) = pos_exp { + let exp_len = len - pos_exp; + if new_coeff_end != pos_exp { + for i in 0..exp_len { + buf[new_coeff_end + i] = buf[pos_exp + i]; + } + } + new_coeff_end + exp_len + } else { + new_coeff_end + }; + // Add back the sign and strip the leading zero. + let result = if let Some(sign) = sign { + if buf[1] == b'0' && buf[2] != b'.' { + buf[1] = sign; + &buf[1..real_end] + } else { + debug_assert!(buf[0] == b'0'); + buf[0] = sign; + &buf[0..real_end] + } + } else { + if buf[0] == b'0' && buf[1] != b'.' { + &buf[1..real_end] + } else { + &buf[0..real_end] + } + }; + // Generate the notation info. + let notation = Notation { + decimal_point: pos_dot < new_coeff_end, + scientific: pos_exp.is_some(), + }; + (result, notation) +} |