summaryrefslogtreecommitdiffstats
path: root/third_party/rust/dtoa-short/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/dtoa-short/src
parentInitial commit. (diff)
downloadfirefox-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.rs197
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)
+}