Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+275
View File
@@ -0,0 +1,275 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// ---
//
// The C++ implementation preserved here in comments is licensed as follows:
//
// Tencent is pleased to support the open source community by making RapidJSON
// available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All
// rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License
// at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
use core::ops::{Mul, Sub};
#[cfg(feature = "no-panic")]
use no_panic::no_panic;
#[derive(Copy, Clone, Debug)]
pub struct DiyFp<F, E> {
pub f: F,
pub e: E,
}
impl<F, E> DiyFp<F, E> {
#[cfg_attr(feature = "no-panic", no_panic)]
pub fn new(f: F, e: E) -> Self {
DiyFp { f, e }
}
}
impl<F, E> Sub for DiyFp<F, E>
where
F: Sub<F, Output = F>,
{
type Output = Self;
#[cfg_attr(feature = "no-panic", no_panic)]
fn sub(self, rhs: Self) -> Self {
DiyFp {
f: self.f - rhs.f,
e: self.e,
}
}
}
impl Mul for DiyFp<u32, i32> {
type Output = Self;
#[cfg_attr(feature = "no-panic", no_panic)]
fn mul(self, rhs: Self) -> Self {
let mut tmp = self.f as u64 * rhs.f as u64;
tmp += 1u64 << 31; // mult_round
DiyFp {
f: (tmp >> 32) as u32,
e: self.e + rhs.e + 32,
}
}
}
impl Mul for DiyFp<u64, isize> {
type Output = Self;
#[cfg_attr(feature = "no-panic", no_panic)]
fn mul(self, rhs: Self) -> Self {
let m32 = 0xFFFFFFFFu64;
let a = self.f >> 32;
let b = self.f & m32;
let c = rhs.f >> 32;
let d = rhs.f & m32;
let ac = a * c;
let bc = b * c;
let ad = a * d;
let bd = b * d;
let mut tmp = (bd >> 32) + (ad & m32) + (bc & m32);
tmp += 1u64 << 31; // mult_round
DiyFp {
f: ac + (ad >> 32) + (bc >> 32) + (tmp >> 32),
e: self.e + rhs.e + 64,
}
}
}
macro_rules! diyfp {
(
floating_type: $fty:ty,
significand_type: $sigty:ty,
exponent_type: $expty:ty,
diy_significand_size: $diy_significand_size:expr,
significand_size: $significand_size:expr,
exponent_bias: $exponent_bias:expr,
mask_type: $mask_type:ty,
exponent_mask: $exponent_mask:expr,
significand_mask: $significand_mask:expr,
hidden_bit: $hidden_bit:expr,
cached_powers_f: $cached_powers_f:expr,
cached_powers_e: $cached_powers_e:expr,
min_power: $min_power:expr,
) => {
type DiyFp = diyfp::DiyFp<$sigty, $expty>;
impl DiyFp {
// Preconditions:
// `d` must have a positive sign and must not be infinity or NaN.
/*
explicit DiyFp(double d) {
union {
double d;
uint64_t u64;
} u = { d };
int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize);
uint64_t significand = (u.u64 & kDpSignificandMask);
if (biased_e != 0) {
f = significand + kDpHiddenBit;
e = biased_e - kDpExponentBias;
}
else {
f = significand;
e = kDpMinExponent + 1;
}
}
*/
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn from(d: $fty) -> Self {
let u: $mask_type = <$fty>::to_bits(d);
let biased_e = ((u & $exponent_mask) >> $significand_size) as $expty;
let significand = u & $significand_mask;
if biased_e != 0 {
DiyFp {
f: significand + $hidden_bit,
e: biased_e - $exponent_bias - $significand_size,
}
} else {
DiyFp {
f: significand,
e: 1 - $exponent_bias - $significand_size,
}
}
}
// Normalizes so that the highest bit of the diy significand is 1.
/*
DiyFp Normalize() const {
DiyFp res = *this;
while (!(res.f & (static_cast<uint64_t>(1) << 63))) {
res.f <<= 1;
res.e--;
}
return res;
}
*/
#[cfg_attr(feature = "no-panic", no_panic)]
fn normalize(self) -> DiyFp {
let mut res = self;
while (res.f & (1 << ($diy_significand_size - 1))) == 0 {
res.f <<= 1;
res.e -= 1;
}
res
}
// Normalizes so that the highest bit of the diy significand is 1.
//
// Precondition:
// `self.f` must be no more than 2 bits longer than the f64 significand.
/*
DiyFp NormalizeBoundary() const {
DiyFp res = *this;
while (!(res.f & (kDpHiddenBit << 1))) {
res.f <<= 1;
res.e--;
}
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
return res;
}
*/
#[cfg_attr(feature = "no-panic", no_panic)]
fn normalize_boundary(self) -> DiyFp {
let mut res = self;
while (res.f & $hidden_bit << 1) == 0 {
res.f <<= 1;
res.e -= 1;
}
res.f <<= $diy_significand_size - $significand_size - 2;
res.e -= $diy_significand_size - $significand_size - 2;
res
}
// Normalizes `self - e` and `self + e` where `e` is half of the least
// significant digit of `self`. The plus is normalized so that the highest
// bit of the diy significand is 1. The minus is normalized so that it has
// the same exponent as the plus.
//
// Preconditions:
// `self` must have been returned directly from `DiyFp::from_f64`.
// `self.f` must not be zero.
/*
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
mi.f <<= mi.e - pl.e;
mi.e = pl.e;
*plus = pl;
*minus = mi;
}
*/
#[cfg_attr(feature = "no-panic", no_panic)]
fn normalized_boundaries(self) -> (DiyFp, DiyFp) {
let pl = DiyFp::new((self.f << 1) + 1, self.e - 1).normalize_boundary();
let mut mi = if self.f == $hidden_bit {
DiyFp::new((self.f << 2) - 1, self.e - 2)
} else {
DiyFp::new((self.f << 1) - 1, self.e - 1)
};
mi.f <<= mi.e - pl.e;
mi.e = pl.e;
(mi, pl)
}
}
/*
inline DiyFp GetCachedPower(int e, int* K) {
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
int k = static_cast<int>(dk);
if (dk - k > 0.0)
k++;
unsigned index = static_cast<unsigned>((k >> 3) + 1);
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
return GetCachedPowerByIndex(index);
}
*/
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
fn get_cached_power(e: $expty) -> (DiyFp, isize) {
let dk = (3 - $diy_significand_size - e) as f64 * 0.30102999566398114f64
- ($min_power + 1) as f64;
let mut k = dk as isize;
if dk - k as f64 > 0.0 {
k += 1;
}
let index = ((k >> 3) + 1) as usize;
let k = -($min_power + (index << 3) as isize);
(
DiyFp::new(*unsafe { $cached_powers_f.get_unchecked(index) }, *unsafe {
$cached_powers_e.get_unchecked(index)
}
as $expty),
k,
)
}
};
}
+523
View File
@@ -0,0 +1,523 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// ---
//
// The C++ implementation preserved here in comments is licensed as follows:
//
// Tencent is pleased to support the open source community by making RapidJSON
// available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All
// rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License
// at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
use core::ptr;
#[cfg(feature = "no-panic")]
use no_panic::no_panic;
/*
inline unsigned CountDecimalDigit32(uint32_t n) {
// Simple pure C++ implementation was faster than __builtin_clz version in this situation.
if (n < 10) return 1;
if (n < 100) return 2;
if (n < 1000) return 3;
if (n < 10000) return 4;
if (n < 100000) return 5;
if (n < 1000000) return 6;
if (n < 10000000) return 7;
if (n < 100000000) return 8;
// Will not reach 10 digits in DigitGen()
//if (n < 1000000000) return 9;
//return 10;
return 9;
}
*/
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
pub fn count_decimal_digit32(n: u32) -> usize {
if n < 10 {
1
} else if n < 100 {
2
} else if n < 1000 {
3
} else if n < 10000 {
4
} else if n < 100000 {
5
} else if n < 1000000 {
6
} else if n < 10000000 {
7
} else if n < 100000000 {
8
}
// Will not reach 10 digits in digit_gen()
else {
9
}
}
/*
inline char* WriteExponent(int K, char* buffer) {
if (K < 0) {
*buffer++ = '-';
K = -K;
}
if (K >= 100) {
*buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
K %= 100;
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
else if (K >= 10) {
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
else
*buffer++ = static_cast<char>('0' + static_cast<char>(K));
return buffer;
}
*/
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn write_exponent(mut k: isize, mut buffer: *mut u8) -> *mut u8 {
if k < 0 {
*buffer = b'-';
buffer = buffer.add(1);
k = -k;
}
if k >= 100 {
*buffer = b'0' + (k / 100) as u8;
k %= 100;
let d = crate::DEC_DIGITS_LUT.as_ptr().offset(k * 2);
ptr::copy_nonoverlapping(d, buffer.add(1), 2);
buffer.add(3)
} else if k >= 10 {
let d = crate::DEC_DIGITS_LUT.as_ptr().offset(k * 2);
ptr::copy_nonoverlapping(d, buffer, 2);
buffer.add(2)
} else {
*buffer = b'0' + k as u8;
buffer.add(1)
}
}
/*
inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
*/
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
pub unsafe fn prettify(buffer: *mut u8, length: isize, k: isize) -> *mut u8 {
let kk = length + k; // 10^(kk-1) <= v < 10^kk
/*
if (0 <= k && kk <= 21) {
// 1234e7 -> 12340000000
for (int i = length; i < kk; i++)
buffer[i] = '0';
buffer[kk] = '.';
buffer[kk + 1] = '0';
return &buffer[kk + 2];
}
*/
if 0 <= k && kk <= 21 {
// 1234e7 -> 12340000000
for i in length..kk {
*buffer.offset(i) = b'0';
}
*buffer.offset(kk) = b'.';
*buffer.offset(kk + 1) = b'0';
buffer.offset(kk + 2)
}
/*
else if (0 < kk && kk <= 21) {
// 1234e-2 -> 12.34
std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
buffer[kk] = '.';
if (0 > k + maxDecimalPlaces) {
// When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
// Remove extra trailing zeros (at least one) after truncation.
for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
if (buffer[i] != '0')
return &buffer[i + 1];
return &buffer[kk + 2]; // Reserve one zero
}
else
return &buffer[length + 1];
}
*/
else if 0 < kk && kk <= 21 {
// 1234e-2 -> 12.34
ptr::copy(
buffer.offset(kk),
buffer.offset(kk + 1),
(length - kk) as usize,
);
*buffer.offset(kk) = b'.';
if 0 > k + crate::MAX_DECIMAL_PLACES {
// When MAX_DECIMAL_PLACES = 2, 1.2345 -> 1.23, 1.102 -> 1.1
// Remove extra trailing zeros (at least one) after truncation.
for i in (kk + 2..kk + crate::MAX_DECIMAL_PLACES + 1).rev() {
if *buffer.offset(i) != b'0' {
return buffer.offset(i + 1);
}
}
buffer.offset(kk + 2) // Reserve one zero
} else {
buffer.offset(length + 1)
}
}
/*
else if (-6 < kk && kk <= 0) {
// 1234e-6 -> 0.001234
const int offset = 2 - kk;
std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
buffer[0] = '0';
buffer[1] = '.';
for (int i = 2; i < offset; i++)
buffer[i] = '0';
if (length - kk > maxDecimalPlaces) {
// When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
// Remove extra trailing zeros (at least one) after truncation.
for (int i = maxDecimalPlaces + 1; i > 2; i--)
if (buffer[i] != '0')
return &buffer[i + 1];
return &buffer[3]; // Reserve one zero
}
else
return &buffer[length + offset];
}
*/
else if -6 < kk && kk <= 0 {
// 1234e-6 -> 0.001234
let offset = 2 - kk;
ptr::copy(buffer, buffer.offset(offset), length as usize);
*buffer = b'0';
*buffer.add(1) = b'.';
for i in 2..offset {
*buffer.offset(i) = b'0';
}
if length - kk > crate::MAX_DECIMAL_PLACES {
// When MAX_DECIMAL_PLACES = 2, 0.123 -> 0.12, 0.102 -> 0.1
// Remove extra trailing zeros (at least one) after truncation.
for i in (3..crate::MAX_DECIMAL_PLACES + 2).rev() {
if *buffer.offset(i) != b'0' {
return buffer.offset(i + 1);
}
}
buffer.add(3) // Reserve one zero
} else {
buffer.offset(length + offset)
}
}
/*
else if (kk < -maxDecimalPlaces) {
// Truncate to zero
buffer[0] = '0';
buffer[1] = '.';
buffer[2] = '0';
return &buffer[3];
}
*/
else if kk < -crate::MAX_DECIMAL_PLACES {
*buffer = b'0';
*buffer.add(1) = b'.';
*buffer.add(2) = b'0';
buffer.add(3)
}
/*
else if (length == 1) {
// 1e30
buffer[1] = 'e';
return WriteExponent(kk - 1, &buffer[2]);
}
*/
else if length == 1 {
// 1e30
*buffer.add(1) = b'e';
write_exponent(kk - 1, buffer.add(2))
}
/*
else {
// 1234e30 -> 1.234e33
std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
buffer[1] = '.';
buffer[length + 1] = 'e';
return WriteExponent(kk - 1, &buffer[0 + length + 2]);
}
*/
else {
// 1234e30 -> 1.234e33
ptr::copy(buffer.add(1), buffer.add(2), (length - 1) as usize);
*buffer.add(1) = b'.';
*buffer.offset(length + 1) = b'e';
write_exponent(kk - 1, buffer.offset(length + 2))
}
}
macro_rules! dtoa {
(
floating_type: $fty:ty,
significand_type: $sigty:ty,
exponent_type: $expty:ty,
$($diyfp_param:ident: $diyfp_value:tt,)*
) => {
diyfp! {
floating_type: $fty,
significand_type: $sigty,
exponent_type: $expty,
$($diyfp_param: $diyfp_value,)*
};
/*
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
while (rest < wp_w && delta - rest >= ten_kappa &&
(rest + ten_kappa < wp_w || /// closer
wp_w - rest > rest + ten_kappa - wp_w)) {
buffer[len - 1]--;
rest += ten_kappa;
}
}
*/
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn grisu_round(buffer: *mut u8, len: isize, delta: $sigty, mut rest: $sigty, ten_kappa: $sigty, wp_w: $sigty) {
while rest < wp_w && delta - rest >= ten_kappa &&
(rest + ten_kappa < wp_w || // closer
wp_w - rest > rest + ten_kappa - wp_w) {
*buffer.offset(len - 1) -= 1;
rest += ten_kappa;
}
}
/*
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
const DiyFp wp_w = Mp - W;
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
uint64_t p2 = Mp.f & (one.f - 1);
unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
*len = 0;
*/
// Returns length and k.
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn digit_gen(w: DiyFp, mp: DiyFp, mut delta: $sigty, buffer: *mut u8, mut k: isize) -> (isize, isize) {
static POW10: [$sigty; 10] = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 ];
let one = DiyFp::new(1 << -mp.e, mp.e);
let wp_w = mp - w;
let mut p1 = (mp.f >> -one.e) as u32;
let mut p2 = mp.f & (one.f - 1);
let mut kappa = dtoa::count_decimal_digit32(p1); // kappa in [0, 9]
let mut len = 0;
/*
while (kappa > 0) {
uint32_t d = 0;
switch (kappa) {
case 9: d = p1 / 100000000; p1 %= 100000000; break;
case 8: d = p1 / 10000000; p1 %= 10000000; break;
case 7: d = p1 / 1000000; p1 %= 1000000; break;
case 6: d = p1 / 100000; p1 %= 100000; break;
case 5: d = p1 / 10000; p1 %= 10000; break;
case 4: d = p1 / 1000; p1 %= 1000; break;
case 3: d = p1 / 100; p1 %= 100; break;
case 2: d = p1 / 10; p1 %= 10; break;
case 1: d = p1; p1 = 0; break;
default:;
}
if (d || *len)
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
kappa--;
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
if (tmp <= delta) {
*K += kappa;
GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
return;
}
}
*/
while kappa > 0 {
let mut d = 0u32;
match kappa {
9 => { d = p1 / 100000000; p1 %= 100000000; }
8 => { d = p1 / 10000000; p1 %= 10000000; }
7 => { d = p1 / 1000000; p1 %= 1000000; }
6 => { d = p1 / 100000; p1 %= 100000; }
5 => { d = p1 / 10000; p1 %= 10000; }
4 => { d = p1 / 1000; p1 %= 1000; }
3 => { d = p1 / 100; p1 %= 100; }
2 => { d = p1 / 10; p1 %= 10; }
1 => { d = p1; p1 = 0; }
_ => {}
}
if d != 0 || len != 0 {
*buffer.offset(len) = b'0' + d as u8;
len += 1;
}
kappa -= 1;
let tmp = ((p1 as $sigty) << -one.e) + p2;
if tmp <= delta {
k += kappa as isize;
grisu_round(buffer, len, delta, tmp, *POW10.get_unchecked(kappa) << -one.e, wp_w.f);
return (len, k);
}
}
// kappa = 0
/*
for (;;) {
p2 *= 10;
delta *= 10;
char d = static_cast<char>(p2 >> -one.e);
if (d || *len)
buffer[(*len)++] = static_cast<char>('0' + d);
p2 &= one.f - 1;
kappa--;
if (p2 < delta) {
*K += kappa;
int index = -static_cast<int>(kappa);
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast<int>(kappa)] : 0));
return;
}
}
*/
loop {
p2 *= 10;
delta *= 10;
let d = (p2 >> -one.e) as u8;
if d != 0 || len != 0 {
*buffer.offset(len) = b'0' + d;
len += 1;
}
p2 &= one.f - 1;
kappa = kappa.wrapping_sub(1);
if p2 < delta {
k += kappa as isize;
let index = -(kappa as isize);
grisu_round(
buffer,
len,
delta,
p2,
one.f,
wp_w.f * if index < 9 {
*POW10.get_unchecked(-(kappa as isize) as usize)
} else {
0
},
);
return (len, k);
}
}
}
/*
inline void Grisu2(double value, char* buffer, int* length, int* K) {
const DiyFp v(value);
DiyFp w_m, w_p;
v.NormalizedBoundaries(&w_m, &w_p);
const DiyFp c_mk = GetCachedPower(w_p.e, K);
const DiyFp W = v.Normalize() * c_mk;
DiyFp Wp = w_p * c_mk;
DiyFp Wm = w_m * c_mk;
Wm.f++;
Wp.f--;
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
}
*/
// Returns length and k.
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn grisu2(value: $fty, buffer: *mut u8) -> (isize, isize) {
let v = DiyFp::from(value);
let (w_m, w_p) = v.normalized_boundaries();
let (c_mk, k) = get_cached_power(w_p.e);
let w = v.normalize() * c_mk;
let mut wp = w_p * c_mk;
let mut wm = w_m * c_mk;
wm.f += 1;
wp.f -= 1;
digit_gen(w, wp, wp.f - wm.f, buffer, k)
}
/*
inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
Double d(value);
if (d.IsZero()) {
if (d.Sign())
*buffer++ = '-'; // -0.0, Issue #289
buffer[0] = '0';
buffer[1] = '.';
buffer[2] = '0';
return &buffer[3];
}
else {
if (value < 0) {
*buffer++ = '-';
value = -value;
}
int length, K;
Grisu2(value, buffer, &length, &K);
return Prettify(buffer, length, K, maxDecimalPlaces);
}
}
*/
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn dtoa(buf: &mut Buffer, mut value: $fty) -> &str {
if value == 0.0 {
if value.is_sign_negative() {
"-0.0"
} else {
"0.0"
}
} else {
let start = buf.bytes.as_mut_ptr().cast::<u8>();
let mut buf_ptr = start;
if value < 0.0 {
*buf_ptr = b'-';
buf_ptr = buf_ptr.add(1);
value = -value;
}
let (length, k) = grisu2(value, buf_ptr);
let end = dtoa::prettify(buf_ptr, length, k);
let len = end as usize - start as usize;
str::from_utf8_unchecked(slice::from_raw_parts(start, len))
}
}
};
}
+354
View File
@@ -0,0 +1,354 @@
//! [![github]](https://github.com/dtolnay/dtoa)&ensp;[![crates-io]](https://crates.io/crates/dtoa)&ensp;[![docs-rs]](https://docs.rs/dtoa)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
//!
//! <br>
//!
//! This crate provides fast conversion of floating point primitives to decimal
//! strings. The implementation is a straightforward Rust port of [Milo Yip]'s
//! C++ implementation [dtoa.h]. The original C++ code of each function is
//! included in comments.
//!
//! See also [`itoa`] for printing integer primitives.
//!
//! [Milo Yip]: https://github.com/miloyip
//! [dtoa.h]: https://github.com/miloyip/rapidjson/blob/master/include/rapidjson/internal/dtoa.h
//! [`itoa`]: https://github.com/dtolnay/itoa
//!
//! # Example
//!
//! ```
//! fn main() {
//! let mut buffer = dtoa::Buffer::new();
//! let printed = buffer.format(2.71828f64);
//! assert_eq!(printed, "2.71828");
//! }
//! ```
//!
//! ## Performance
//!
//! The [dtoa-benchmark] compares this library and other Rust floating point
//! formatting implementations across a range of precisions. The vertical axis
//! in this chart shows nanoseconds taken by a single execution of
//! `dtoa::Buffer::new().format_finite(value)` so a lower result indicates a
//! faster library.
//!
//! [dtoa-benchmark]: https://github.com/dtolnay/dtoa-benchmark
//!
//! ![performance](https://raw.githubusercontent.com/dtolnay/dtoa/master/dtoa-benchmark.png)
#![doc(html_root_url = "https://docs.rs/dtoa/1.0.11")]
#![no_std]
#![allow(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
clippy::doc_markdown,
clippy::expl_impl_clone_on_copy,
clippy::if_not_else,
clippy::missing_errors_doc,
clippy::must_use_candidate,
clippy::needless_doctest_main,
clippy::range_plus_one,
clippy::semicolon_if_nothing_returned, // https://github.com/rust-lang/rust-clippy/issues/7768
clippy::shadow_unrelated,
clippy::suspicious_else_formatting,
clippy::unreadable_literal,
clippy::unseparated_literal_suffix
)]
#[macro_use]
mod diyfp;
#[macro_use]
mod dtoa;
use core::mem::MaybeUninit;
use core::slice;
use core::str;
#[cfg(feature = "no-panic")]
use no_panic::no_panic;
const NAN: &str = "NaN";
const INFINITY: &str = "inf";
const NEG_INFINITY: &str = "-inf";
/// A correctly sized stack allocation for the formatted float to be written
/// into.
///
/// # Example
///
/// ```
/// let mut buffer = dtoa::Buffer::new();
/// let printed = buffer.format_finite(2.71828);
/// assert_eq!(printed, "2.71828");
/// ```
pub struct Buffer {
bytes: [MaybeUninit<u8>; 25],
}
impl Default for Buffer {
#[inline]
fn default() -> Buffer {
Buffer::new()
}
}
impl Copy for Buffer {}
#[allow(clippy::non_canonical_clone_impl)]
impl Clone for Buffer {
#[inline]
fn clone(&self) -> Self {
Buffer::new()
}
}
impl Buffer {
/// This is a cheap operation; you don't need to worry about reusing buffers
/// for efficiency.
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
pub fn new() -> Buffer {
let bytes = [MaybeUninit::<u8>::uninit(); 25];
Buffer { bytes }
}
/// Print a floating point number into this buffer and return a reference to
/// its string representation within the buffer.
///
/// # Special cases
///
/// This function formats NaN as the string "NaN", positive infinity as
/// "inf", and negative infinity as "-inf" to match std::fmt.
///
/// If your input is known to be finite, you may get better performance by
/// calling the `format_finite` method instead of `format` to avoid the
/// checks for special cases.
#[cfg_attr(feature = "no-panic", no_panic)]
pub fn format<F: Float>(&mut self, value: F) -> &str {
if value.is_nonfinite() {
value.format_nonfinite()
} else {
self.format_finite(value)
}
}
/// Print a floating point number into this buffer and return a reference to
/// its string representation within the buffer.
///
/// # Special cases
///
/// This function **does not** check for NaN or infinity. If the input
/// number is not a finite float, the printed representation will be some
/// correctly formatted but unspecified numerical value.
///
/// Please check [`is_finite`] yourself before calling this function, or
/// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
///
/// [`is_finite`]: f64::is_finite
/// [`is_nan`]: f64::is_nan
/// [`is_infinite`]: f64::is_infinite
#[cfg_attr(feature = "no-panic", no_panic)]
pub fn format_finite<F: Float>(&mut self, value: F) -> &str {
value.write(self)
}
}
/// A floating point number that can be written into a [`dtoa::Buffer`][Buffer].
///
/// This trait is sealed and cannot be implemented for types outside of dtoa.
pub trait Float: private::Sealed {}
impl Float for f32 {}
impl Float for f64 {}
// Seal to prevent downstream implementations of Float trait.
mod private {
pub trait Sealed: Copy {
fn is_nonfinite(self) -> bool;
fn format_nonfinite(self) -> &'static str;
fn write(self, buf: &mut crate::Buffer) -> &str;
}
}
impl private::Sealed for f32 {
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
fn is_nonfinite(self) -> bool {
const EXP_MASK: u32 = 0x7f800000;
let bits = self.to_bits();
bits & EXP_MASK == EXP_MASK
}
#[cold]
#[cfg_attr(feature = "no-panic", no_panic)]
fn format_nonfinite(self) -> &'static str {
const MANTISSA_MASK: u32 = 0x007fffff;
const SIGN_MASK: u32 = 0x80000000;
let bits = self.to_bits();
if bits & MANTISSA_MASK != 0 {
NAN
} else if bits & SIGN_MASK != 0 {
NEG_INFINITY
} else {
INFINITY
}
}
#[inline]
fn write(self, buf: &mut Buffer) -> &str {
dtoa! {
floating_type: f32,
significand_type: u32,
exponent_type: i32,
diy_significand_size: 32,
significand_size: 23,
exponent_bias: 0x7F,
mask_type: u32,
exponent_mask: 0x7F800000,
significand_mask: 0x007FFFFF,
hidden_bit: 0x00800000,
cached_powers_f: CACHED_POWERS_F_32,
cached_powers_e: CACHED_POWERS_E_32,
min_power: (-36),
};
unsafe { dtoa(buf, self) }
}
}
impl private::Sealed for f64 {
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
fn is_nonfinite(self) -> bool {
const EXP_MASK: u64 = 0x7ff0000000000000;
let bits = self.to_bits();
bits & EXP_MASK == EXP_MASK
}
#[cold]
#[cfg_attr(feature = "no-panic", no_panic)]
fn format_nonfinite(self) -> &'static str {
const MANTISSA_MASK: u64 = 0x000fffffffffffff;
const SIGN_MASK: u64 = 0x8000000000000000;
let bits = self.to_bits();
if bits & MANTISSA_MASK != 0 {
NAN
} else if bits & SIGN_MASK != 0 {
NEG_INFINITY
} else {
INFINITY
}
}
#[inline]
fn write(self, buf: &mut Buffer) -> &str {
dtoa! {
floating_type: f64,
significand_type: u64,
exponent_type: isize,
diy_significand_size: 64,
significand_size: 52,
exponent_bias: 0x3FF,
mask_type: u64,
exponent_mask: 0x7FF0000000000000,
significand_mask: 0x000FFFFFFFFFFFFF,
hidden_bit: 0x0010000000000000,
cached_powers_f: CACHED_POWERS_F_64,
cached_powers_e: CACHED_POWERS_E_64,
min_power: (-348),
};
unsafe { dtoa(buf, self) }
}
}
////////////////////////////////////////////////////////////////////////////////
const MAX_DECIMAL_PLACES: isize = 324;
static DEC_DIGITS_LUT: [u8; 200] = *b"\
0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\
4041424344454647484950515253545556575859\
6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899";
// 10^-36, 10^-28, ..., 10^52
#[rustfmt::skip]
static CACHED_POWERS_F_32: [u32; 12] = [
0xaa242499, 0xfd87b5f3, 0xbce50865, 0x8cbccc09,
0xd1b71759, 0x9c400000, 0xe8d4a510, 0xad78ebc6,
0x813f3979, 0xc097ce7c, 0x8f7e32ce, 0xd5d238a5,
];
#[rustfmt::skip]
static CACHED_POWERS_E_32: [i16; 12] = [
-151, -125, -98, -71, -45, -18, 8, 35, 62, 88, 115, 141,
];
// 10^-348, 10^-340, ..., 10^340
#[rustfmt::skip]
static CACHED_POWERS_F_64: [u64; 87] = [
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76,
0x8b16fb203055ac76, 0xcf42894a5dce35ea,
0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f,
0xbe5691ef416bd60c, 0x8dd01fad907ffc3c,
0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
0xea9c227723ee8bcb, 0xaecc49914078536d,
0x823c12795db6ce57, 0xc21094364dfb5637,
0x9096ea6f3848984f, 0xd77485cb25823ac7,
0xa086cfcd97bf97f4, 0xef340a98172aace5,
0xb23867fb2a35b28e, 0x84c8d4dfd2c63f3b,
0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6,
0xf3e2f893dec3f126, 0xb5b5ada8aaff80b8,
0x87625f056c7c4a8b, 0xc9bcff6034c13053,
0x964e858c91ba2655, 0xdff9772470297ebd,
0xa6dfbd9fb8e5b88f, 0xf8a95fcf88747d94,
0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
0xcdb02555653131b6, 0x993fe2c6d07b7fac,
0xe45c10c42a2b3b06, 0xaa242499697392d3,
0xfd87b5f28300ca0e, 0xbce5086492111aeb,
0x8cbccc096f5088cc, 0xd1b71758e219652c,
0x9c40000000000000, 0xe8d4a51000000000,
0xad78ebc5ac620000, 0x813f3978f8940984,
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70,
0xd5d238a4abe98068, 0x9f4f2726179a2245,
0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
0x83c7088e1aab65db, 0xc45d1df942711d9a,
0x924d692ca61be758, 0xda01ee641a708dea,
0xa26da3999aef774a, 0xf209787bb47d6b85,
0xb454e4a179dd1877, 0x865b86925b9bc5c2,
0xc83553c5c8965d3d, 0x952ab45cfa97a0b3,
0xde469fbd99a05fe3, 0xa59bc234db398c25,
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece,
0x88fcf317f22241e2, 0xcc20ce9bd35c78a5,
0x98165af37b2153df, 0xe2a0b5dc971f303a,
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c,
0xbb764c4ca7a44410, 0x8bab8eefb6409c1a,
0xd01fef10a657842c, 0x9b10a4e5e9913129,
0xe7109bfba19c0c9d, 0xac2820d9623bf429,
0x80444b5e7aa7cf85, 0xbf21e44003acdd2d,
0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9,
0xaf87023b9bf0ee6b,
];
#[rustfmt::skip]
static CACHED_POWERS_E_64: [i16; 87] = [
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
-954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
-688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
-422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
-157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
907, 933, 960, 986, 1013, 1039, 1066,
];