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-1.0.11/src/dtoa.rs
Line
Count
Source
1
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4
// option. This file may not be copied, modified, or distributed
5
// except according to those terms.
6
//
7
// ---
8
//
9
// The C++ implementation preserved here in comments is licensed as follows:
10
//
11
// Tencent is pleased to support the open source community by making RapidJSON
12
// available.
13
//
14
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All
15
// rights reserved.
16
//
17
// Licensed under the MIT License (the "License"); you may not use this file
18
// except in compliance with the License. You may obtain a copy of the License
19
// at
20
//
21
// http://opensource.org/licenses/MIT
22
//
23
// Unless required by applicable law or agreed to in writing, software
24
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
25
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
26
// License for the specific language governing permissions and limitations under
27
// the License.
28
29
use core::ptr;
30
#[cfg(feature = "no-panic")]
31
use no_panic::no_panic;
32
33
/*
34
inline unsigned CountDecimalDigit32(uint32_t n) {
35
    // Simple pure C++ implementation was faster than __builtin_clz version in this situation.
36
    if (n < 10) return 1;
37
    if (n < 100) return 2;
38
    if (n < 1000) return 3;
39
    if (n < 10000) return 4;
40
    if (n < 100000) return 5;
41
    if (n < 1000000) return 6;
42
    if (n < 10000000) return 7;
43
    if (n < 100000000) return 8;
44
    // Will not reach 10 digits in DigitGen()
45
    //if (n < 1000000000) return 9;
46
    //return 10;
47
    return 9;
48
}
49
*/
50
51
#[inline]
52
#[cfg_attr(feature = "no-panic", no_panic)]
53
0
pub fn count_decimal_digit32(n: u32) -> usize {
54
0
    if n < 10 {
55
0
        1
56
0
    } else if n < 100 {
57
0
        2
58
0
    } else if n < 1000 {
59
0
        3
60
0
    } else if n < 10000 {
61
0
        4
62
0
    } else if n < 100000 {
63
0
        5
64
0
    } else if n < 1000000 {
65
0
        6
66
0
    } else if n < 10000000 {
67
0
        7
68
0
    } else if n < 100000000 {
69
0
        8
70
    }
71
    // Will not reach 10 digits in digit_gen()
72
    else {
73
0
        9
74
    }
75
0
}
Unexecuted instantiation: dtoa::dtoa::count_decimal_digit32
Unexecuted instantiation: dtoa::dtoa::count_decimal_digit32
76
77
/*
78
inline char* WriteExponent(int K, char* buffer) {
79
    if (K < 0) {
80
        *buffer++ = '-';
81
        K = -K;
82
    }
83
84
    if (K >= 100) {
85
        *buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
86
        K %= 100;
87
        const char* d = GetDigitsLut() + K * 2;
88
        *buffer++ = d[0];
89
        *buffer++ = d[1];
90
    }
91
    else if (K >= 10) {
92
        const char* d = GetDigitsLut() + K * 2;
93
        *buffer++ = d[0];
94
        *buffer++ = d[1];
95
    }
96
    else
97
        *buffer++ = static_cast<char>('0' + static_cast<char>(K));
98
99
    return buffer;
100
}
101
*/
102
103
#[inline]
104
#[cfg_attr(feature = "no-panic", no_panic)]
105
0
unsafe fn write_exponent(mut k: isize, mut buffer: *mut u8) -> *mut u8 {
106
0
    if k < 0 {
107
0
        *buffer = b'-';
108
0
        buffer = buffer.add(1);
109
0
        k = -k;
110
0
    }
111
112
0
    if k >= 100 {
113
0
        *buffer = b'0' + (k / 100) as u8;
114
0
        k %= 100;
115
0
        let d = crate::DEC_DIGITS_LUT.as_ptr().offset(k * 2);
116
0
        ptr::copy_nonoverlapping(d, buffer.add(1), 2);
117
0
        buffer.add(3)
118
0
    } else if k >= 10 {
119
0
        let d = crate::DEC_DIGITS_LUT.as_ptr().offset(k * 2);
120
0
        ptr::copy_nonoverlapping(d, buffer, 2);
121
0
        buffer.add(2)
122
    } else {
123
0
        *buffer = b'0' + k as u8;
124
0
        buffer.add(1)
125
    }
126
0
}
Unexecuted instantiation: dtoa::dtoa::write_exponent
Unexecuted instantiation: dtoa::dtoa::write_exponent
127
128
/*
129
inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
130
    const int kk = length + k;  // 10^(kk-1) <= v < 10^kk
131
*/
132
133
#[inline]
134
#[cfg_attr(feature = "no-panic", no_panic)]
135
0
pub unsafe fn prettify(buffer: *mut u8, length: isize, k: isize) -> *mut u8 {
136
0
    let kk = length + k; // 10^(kk-1) <= v < 10^kk
137
138
    /*
139
    if (0 <= k && kk <= 21) {
140
        // 1234e7 -> 12340000000
141
        for (int i = length; i < kk; i++)
142
            buffer[i] = '0';
143
        buffer[kk] = '.';
144
        buffer[kk + 1] = '0';
145
        return &buffer[kk + 2];
146
    }
147
    */
148
0
    if 0 <= k && kk <= 21 {
149
        // 1234e7 -> 12340000000
150
0
        for i in length..kk {
151
0
            *buffer.offset(i) = b'0';
152
0
        }
153
0
        *buffer.offset(kk) = b'.';
154
0
        *buffer.offset(kk + 1) = b'0';
155
0
        buffer.offset(kk + 2)
156
    }
157
    /*
158
    else if (0 < kk && kk <= 21) {
159
        // 1234e-2 -> 12.34
160
        std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
161
        buffer[kk] = '.';
162
        if (0 > k + maxDecimalPlaces) {
163
            // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
164
            // Remove extra trailing zeros (at least one) after truncation.
165
            for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
166
                if (buffer[i] != '0')
167
                    return &buffer[i + 1];
168
            return &buffer[kk + 2]; // Reserve one zero
169
        }
170
        else
171
            return &buffer[length + 1];
172
    }
173
    */
174
0
    else if 0 < kk && kk <= 21 {
175
        // 1234e-2 -> 12.34
176
0
        ptr::copy(
177
0
            buffer.offset(kk),
178
0
            buffer.offset(kk + 1),
179
0
            (length - kk) as usize,
180
        );
181
0
        *buffer.offset(kk) = b'.';
182
0
        if 0 > k + crate::MAX_DECIMAL_PLACES {
183
            // When MAX_DECIMAL_PLACES = 2, 1.2345 -> 1.23, 1.102 -> 1.1
184
            // Remove extra trailing zeros (at least one) after truncation.
185
0
            for i in (kk + 2..kk + crate::MAX_DECIMAL_PLACES + 1).rev() {
186
0
                if *buffer.offset(i) != b'0' {
187
0
                    return buffer.offset(i + 1);
188
0
                }
189
            }
190
0
            buffer.offset(kk + 2) // Reserve one zero
191
        } else {
192
0
            buffer.offset(length + 1)
193
        }
194
    }
195
    /*
196
    else if (-6 < kk && kk <= 0) {
197
        // 1234e-6 -> 0.001234
198
        const int offset = 2 - kk;
199
        std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
200
        buffer[0] = '0';
201
        buffer[1] = '.';
202
        for (int i = 2; i < offset; i++)
203
            buffer[i] = '0';
204
        if (length - kk > maxDecimalPlaces) {
205
            // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
206
            // Remove extra trailing zeros (at least one) after truncation.
207
            for (int i = maxDecimalPlaces + 1; i > 2; i--)
208
                if (buffer[i] != '0')
209
                    return &buffer[i + 1];
210
            return &buffer[3]; // Reserve one zero
211
        }
212
        else
213
            return &buffer[length + offset];
214
    }
215
    */
216
0
    else if -6 < kk && kk <= 0 {
217
        // 1234e-6 -> 0.001234
218
0
        let offset = 2 - kk;
219
0
        ptr::copy(buffer, buffer.offset(offset), length as usize);
220
0
        *buffer = b'0';
221
0
        *buffer.add(1) = b'.';
222
0
        for i in 2..offset {
223
0
            *buffer.offset(i) = b'0';
224
0
        }
225
0
        if length - kk > crate::MAX_DECIMAL_PLACES {
226
            // When MAX_DECIMAL_PLACES = 2, 0.123 -> 0.12, 0.102 -> 0.1
227
            // Remove extra trailing zeros (at least one) after truncation.
228
0
            for i in (3..crate::MAX_DECIMAL_PLACES + 2).rev() {
229
0
                if *buffer.offset(i) != b'0' {
230
0
                    return buffer.offset(i + 1);
231
0
                }
232
            }
233
0
            buffer.add(3) // Reserve one zero
234
        } else {
235
0
            buffer.offset(length + offset)
236
        }
237
    }
238
    /*
239
    else if (kk < -maxDecimalPlaces) {
240
        // Truncate to zero
241
        buffer[0] = '0';
242
        buffer[1] = '.';
243
        buffer[2] = '0';
244
        return &buffer[3];
245
    }
246
    */
247
0
    else if kk < -crate::MAX_DECIMAL_PLACES {
248
0
        *buffer = b'0';
249
0
        *buffer.add(1) = b'.';
250
0
        *buffer.add(2) = b'0';
251
0
        buffer.add(3)
252
    }
253
    /*
254
    else if (length == 1) {
255
        // 1e30
256
        buffer[1] = 'e';
257
        return WriteExponent(kk - 1, &buffer[2]);
258
    }
259
    */
260
0
    else if length == 1 {
261
        // 1e30
262
0
        *buffer.add(1) = b'e';
263
0
        write_exponent(kk - 1, buffer.add(2))
264
    }
265
    /*
266
    else {
267
        // 1234e30 -> 1.234e33
268
        std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
269
        buffer[1] = '.';
270
        buffer[length + 1] = 'e';
271
        return WriteExponent(kk - 1, &buffer[0 + length + 2]);
272
    }
273
    */
274
    else {
275
        // 1234e30 -> 1.234e33
276
0
        ptr::copy(buffer.add(1), buffer.add(2), (length - 1) as usize);
277
0
        *buffer.add(1) = b'.';
278
0
        *buffer.offset(length + 1) = b'e';
279
0
        write_exponent(kk - 1, buffer.offset(length + 2))
280
    }
281
0
}
Unexecuted instantiation: dtoa::dtoa::prettify
Unexecuted instantiation: dtoa::dtoa::prettify
282
283
macro_rules! dtoa {
284
    (
285
        floating_type: $fty:ty,
286
        significand_type: $sigty:ty,
287
        exponent_type: $expty:ty,
288
        $($diyfp_param:ident: $diyfp_value:tt,)*
289
    ) => {
290
        diyfp! {
291
            floating_type: $fty,
292
            significand_type: $sigty,
293
            exponent_type: $expty,
294
            $($diyfp_param: $diyfp_value,)*
295
        };
296
297
        /*
298
        inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
299
            while (rest < wp_w && delta - rest >= ten_kappa &&
300
                (rest + ten_kappa < wp_w ||  /// closer
301
                    wp_w - rest > rest + ten_kappa - wp_w)) {
302
                buffer[len - 1]--;
303
                rest += ten_kappa;
304
            }
305
        }
306
        */
307
308
        #[inline]
309
        #[cfg_attr(feature = "no-panic", no_panic)]
310
0
        unsafe fn grisu_round(buffer: *mut u8, len: isize, delta: $sigty, mut rest: $sigty, ten_kappa: $sigty, wp_w: $sigty) {
311
0
            while rest < wp_w && delta - rest >= ten_kappa &&
312
0
                (rest + ten_kappa < wp_w || // closer
313
0
                    wp_w - rest > rest + ten_kappa - wp_w) {
314
0
                *buffer.offset(len - 1) -= 1;
315
0
                rest += ten_kappa;
316
0
            }
317
0
        }
Unexecuted instantiation: <f32 as dtoa::private::Sealed>::write::grisu_round
Unexecuted instantiation: <f32 as dtoa::private::Sealed>::write::grisu_round
Unexecuted instantiation: <f64 as dtoa::private::Sealed>::write::grisu_round
318
319
        /*
320
        inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
321
            static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
322
            const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
323
            const DiyFp wp_w = Mp - W;
324
            uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
325
            uint64_t p2 = Mp.f & (one.f - 1);
326
            unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
327
            *len = 0;
328
        */
329
330
        // Returns length and k.
331
        #[inline]
332
        #[cfg_attr(feature = "no-panic", no_panic)]
333
0
        unsafe fn digit_gen(w: DiyFp, mp: DiyFp, mut delta: $sigty, buffer: *mut u8, mut k: isize) -> (isize, isize) {
334
            static POW10: [$sigty; 10] = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 ];
335
0
            let one = DiyFp::new(1 << -mp.e, mp.e);
336
0
            let wp_w = mp - w;
337
0
            let mut p1 = (mp.f >> -one.e) as u32;
338
0
            let mut p2 = mp.f & (one.f - 1);
339
0
            let mut kappa = dtoa::count_decimal_digit32(p1); // kappa in [0, 9]
340
0
            let mut len = 0;
341
342
            /*
343
            while (kappa > 0) {
344
                uint32_t d = 0;
345
                switch (kappa) {
346
                    case  9: d = p1 /  100000000; p1 %=  100000000; break;
347
                    case  8: d = p1 /   10000000; p1 %=   10000000; break;
348
                    case  7: d = p1 /    1000000; p1 %=    1000000; break;
349
                    case  6: d = p1 /     100000; p1 %=     100000; break;
350
                    case  5: d = p1 /      10000; p1 %=      10000; break;
351
                    case  4: d = p1 /       1000; p1 %=       1000; break;
352
                    case  3: d = p1 /        100; p1 %=        100; break;
353
                    case  2: d = p1 /         10; p1 %=         10; break;
354
                    case  1: d = p1;              p1 =           0; break;
355
                    default:;
356
                }
357
                if (d || *len)
358
                    buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
359
                kappa--;
360
                uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
361
                if (tmp <= delta) {
362
                    *K += kappa;
363
                    GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
364
                    return;
365
                }
366
            }
367
            */
368
0
            while kappa > 0 {
369
0
                let mut d = 0u32;
370
0
                match kappa {
371
0
                    9 => { d = p1 /  100000000; p1 %=  100000000; }
372
0
                    8 => { d = p1 /   10000000; p1 %=   10000000; }
373
0
                    7 => { d = p1 /    1000000; p1 %=    1000000; }
374
0
                    6 => { d = p1 /     100000; p1 %=     100000; }
375
0
                    5 => { d = p1 /      10000; p1 %=      10000; }
376
0
                    4 => { d = p1 /       1000; p1 %=       1000; }
377
0
                    3 => { d = p1 /        100; p1 %=        100; }
378
0
                    2 => { d = p1 /         10; p1 %=         10; }
379
0
                    1 => { d = p1;              p1 =           0; }
380
0
                    _ => {}
381
                }
382
0
                if d != 0 || len != 0 {
383
0
                    *buffer.offset(len) = b'0' + d as u8;
384
0
                    len += 1;
385
0
                }
386
0
                kappa -= 1;
387
0
                let tmp = ((p1 as $sigty) << -one.e) + p2;
388
0
                if tmp <= delta {
389
0
                    k += kappa as isize;
390
0
                    grisu_round(buffer, len, delta, tmp, *POW10.get_unchecked(kappa) << -one.e, wp_w.f);
391
0
                    return (len, k);
392
0
                }
393
            }
394
395
            // kappa = 0
396
            /*
397
            for (;;) {
398
                p2 *= 10;
399
                delta *= 10;
400
                char d = static_cast<char>(p2 >> -one.e);
401
                if (d || *len)
402
                    buffer[(*len)++] = static_cast<char>('0' + d);
403
                p2 &= one.f - 1;
404
                kappa--;
405
                if (p2 < delta) {
406
                    *K += kappa;
407
                    int index = -static_cast<int>(kappa);
408
                    GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast<int>(kappa)] : 0));
409
                    return;
410
                }
411
            }
412
            */
413
            loop {
414
0
                p2 *= 10;
415
0
                delta *= 10;
416
0
                let d = (p2 >> -one.e) as u8;
417
0
                if d != 0 || len != 0 {
418
0
                    *buffer.offset(len) = b'0' + d;
419
0
                    len += 1;
420
0
                }
421
0
                p2 &= one.f - 1;
422
0
                kappa = kappa.wrapping_sub(1);
423
0
                if p2 < delta {
424
0
                    k += kappa as isize;
425
0
                    let index = -(kappa as isize);
426
0
                    grisu_round(
427
0
                        buffer,
428
0
                        len,
429
0
                        delta,
430
0
                        p2,
431
0
                        one.f,
432
0
                        wp_w.f * if index < 9 {
433
0
                            *POW10.get_unchecked(-(kappa as isize) as usize)
434
                        } else {
435
0
                            0
436
                        },
437
                    );
438
0
                    return (len, k);
439
0
                }
440
            }
441
0
        }
Unexecuted instantiation: <f32 as dtoa::private::Sealed>::write::digit_gen
Unexecuted instantiation: <f32 as dtoa::private::Sealed>::write::digit_gen
Unexecuted instantiation: <f64 as dtoa::private::Sealed>::write::digit_gen
442
443
        /*
444
        inline void Grisu2(double value, char* buffer, int* length, int* K) {
445
            const DiyFp v(value);
446
            DiyFp w_m, w_p;
447
            v.NormalizedBoundaries(&w_m, &w_p);
448
449
            const DiyFp c_mk = GetCachedPower(w_p.e, K);
450
            const DiyFp W = v.Normalize() * c_mk;
451
            DiyFp Wp = w_p * c_mk;
452
            DiyFp Wm = w_m * c_mk;
453
            Wm.f++;
454
            Wp.f--;
455
            DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
456
        }
457
        */
458
459
        // Returns length and k.
460
        #[inline]
461
        #[cfg_attr(feature = "no-panic", no_panic)]
462
0
        unsafe fn grisu2(value: $fty, buffer: *mut u8) -> (isize, isize) {
463
0
            let v = DiyFp::from(value);
464
0
            let (w_m, w_p) = v.normalized_boundaries();
465
466
0
            let (c_mk, k) = get_cached_power(w_p.e);
467
0
            let w = v.normalize() * c_mk;
468
0
            let mut wp = w_p * c_mk;
469
0
            let mut wm = w_m * c_mk;
470
0
            wm.f += 1;
471
0
            wp.f -= 1;
472
0
            digit_gen(w, wp, wp.f - wm.f, buffer, k)
473
0
        }
Unexecuted instantiation: <f32 as dtoa::private::Sealed>::write::grisu2
Unexecuted instantiation: <f32 as dtoa::private::Sealed>::write::grisu2
Unexecuted instantiation: <f64 as dtoa::private::Sealed>::write::grisu2
474
475
        /*
476
        inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
477
            RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
478
            Double d(value);
479
            if (d.IsZero()) {
480
                if (d.Sign())
481
                    *buffer++ = '-';     // -0.0, Issue #289
482
                buffer[0] = '0';
483
                buffer[1] = '.';
484
                buffer[2] = '0';
485
                return &buffer[3];
486
            }
487
            else {
488
                if (value < 0) {
489
                    *buffer++ = '-';
490
                    value = -value;
491
                }
492
                int length, K;
493
                Grisu2(value, buffer, &length, &K);
494
                return Prettify(buffer, length, K, maxDecimalPlaces);
495
            }
496
        }
497
        */
498
499
        #[inline]
500
        #[cfg_attr(feature = "no-panic", no_panic)]
501
0
        unsafe fn dtoa(buf: &mut Buffer, mut value: $fty) -> &str {
502
0
            if value == 0.0 {
503
0
                if value.is_sign_negative() {
504
0
                    "-0.0"
505
                } else {
506
0
                    "0.0"
507
                }
508
            } else {
509
0
                let start = buf.bytes.as_mut_ptr().cast::<u8>();
510
0
                let mut buf_ptr = start;
511
0
                if value < 0.0 {
512
0
                    *buf_ptr = b'-';
513
0
                    buf_ptr = buf_ptr.add(1);
514
0
                    value = -value;
515
0
                }
516
0
                let (length, k) = grisu2(value, buf_ptr);
517
0
                let end = dtoa::prettify(buf_ptr, length, k);
518
0
                let len = end as usize - start as usize;
519
0
                str::from_utf8_unchecked(slice::from_raw_parts(start, len))
520
            }
521
0
        }
Unexecuted instantiation: <f32 as dtoa::private::Sealed>::write::dtoa
Unexecuted instantiation: <f32 as dtoa::private::Sealed>::write::dtoa
Unexecuted instantiation: <f64 as dtoa::private::Sealed>::write::dtoa
522
    };
523
}