Coverage Report

Created: 2026-02-14 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/jiff-0.2.20/src/fmt/buffer.rs
Line
Count
Source
1
use core::mem::MaybeUninit;
2
3
use crate::{fmt::Write, Error};
4
5
const MAX_CAPACITY: usize = u16::MAX as usize;
6
// From `u64::MAX.to_string().len()`.
7
const MAX_INTEGER_LEN: u8 = 20;
8
const MAX_PRECISION: usize = 9;
9
10
/// The minimum buffer length required for *any* of `BorrowedBuffer`'s
11
/// integer formatting APIs to work.
12
///
13
/// This relies on the fact that the maximum padding is clamped to `20`.
14
const BROAD_MINIMUM_BUFFER_LEN: usize = 20;
15
16
/// All integers in the range `0..=99`, zero padded.
17
const RADIX_100_ZERO: [u8; 200] = *b"00010203040506070809\
18
       10111213141516171819\
19
       20212223242526272829\
20
       30313233343536373839\
21
       40414243444546474849\
22
       50515253545556575859\
23
       60616263646566676869\
24
       70717273747576777879\
25
       80818283848586878889\
26
       90919293949596979899";
27
28
/// All integers in the range `0..=99`, space padded.
29
const RADIX_100_SPACE: [u8; 200] = *b" 0 1 2 3 4 5 6 7 8 9\
30
       10111213141516171819\
31
       20212223242526272829\
32
       30313233343536373839\
33
       40414243444546474849\
34
       50515253545556575859\
35
       60616263646566676869\
36
       70717273747576777879\
37
       80818283848586878889\
38
       90919293949596979899";
39
40
/// An uninitialized slice of bytes of fixed size.
41
///
42
/// This is typically used with `BorrowedBuffer`.
43
#[derive(Clone, Copy)]
44
pub(crate) struct ArrayBuffer<const N: usize> {
45
    data: [MaybeUninit<u8>; N],
46
}
47
48
impl<const N: usize> ArrayBuffer<N> {
49
    /// Return a writable buffer into this storage.
50
    #[cfg_attr(feature = "perf-inline", inline(always))]
51
0
    pub(crate) fn as_borrowed<'data>(&mut self) -> BorrowedBuffer<'_> {
52
0
        BorrowedBuffer::from(&mut self.data)
53
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<19>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<35>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<73>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<78>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<9>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<100>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<306>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<190>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<194>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<18>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<29>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<31>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<32>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<33>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<38>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<72>>::as_borrowed
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<13>>::as_borrowed
54
55
    /// Assume this entire buffer is initialized and return it as an array.
56
    ///
57
    /// # Safety
58
    ///
59
    /// Callers must ensure the entire buffer is initialized.
60
0
    unsafe fn assume_init(self) -> [u8; N] {
61
        // SAFETY: Callers are responsible for ensuring that `self.data`
62
        // is initialized. Otherwise, `MaybeUninit<u8>` and `u8` have the
63
        // same representation.
64
        unsafe {
65
0
            *(&self.data as *const [MaybeUninit<u8>; N] as *const [u8; N])
66
        }
67
0
    }
68
}
69
70
/// Construct an uninitialized buffer of data of size `N`.
71
impl<const N: usize> Default for ArrayBuffer<N> {
72
    #[cfg_attr(feature = "perf-inline", inline(always))]
73
0
    fn default() -> ArrayBuffer<N> {
74
0
        ArrayBuffer { data: [MaybeUninit::uninit(); N] }
75
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<19> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<35> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<73> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<78> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<100> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<306> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<190> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<194> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<18> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<29> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<31> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<32> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<33> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<38> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<72> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<9> as core::default::Default>::default
Unexecuted instantiation: <jiff::fmt::buffer::ArrayBuffer<13> as core::default::Default>::default
76
}
77
78
/// A borrowed buffer for writing into an uninitialized slice of bytes.
79
///
80
/// This can be used with, e.g., an `ArrayBuffer` as backing storage. This
81
/// type will managed actually writing to the backing storage, keeping track
82
/// of how much data has been written and exposing a safe API.
83
///
84
/// This type is principally used in Jiff's printer implementations. In
85
/// particular, this helps printers generate tighter and more efficient code.
86
/// Once printing is done, the data in the buffer is then copied to the caller
87
/// provided implementation of `jiff::fmt::Write`. This double write is
88
/// unfortunate, but it turned out that threading a `jiff::fmt::Write` down
89
/// through the printers and using it directly leads to slower code overall
90
/// *and* more code bloat. This is because a `BorrowedBuffer` is an incredibly
91
/// lightweight abstraction that never has to deal with I/O or growing an
92
/// allocation.
93
///
94
/// # Design
95
///
96
/// * Requires valid UTF-8 so that we can provide higher level safe APIs for
97
/// interfacing with `String`.
98
/// * Specifically panics when a write would exceed available capacity. This
99
/// introduces a branch, but effectively decouples "get the maximum size
100
/// correct" from "is memory safe."
101
#[derive(Debug)]
102
pub(crate) struct BorrowedBuffer<'data> {
103
    data: &'data mut [MaybeUninit<u8>],
104
    filled: u16,
105
}
106
107
impl<'data> BorrowedBuffer<'data> {
108
    /// A high level API for writing to a `jiff::fmt::Write` via a buffer of
109
    /// uninitialized bytes.
110
    ///
111
    /// When the `alloc` crate feature is enabled and `W` provides a
112
    /// `&mut Vec<u8>`, then the buffer is extracted directly from the
113
    /// spare capacity of the `Vec<u8>`.
114
    #[cfg_attr(feature = "perf-inline", inline(always))]
115
0
    pub(crate) fn with_writer<const N: usize>(
116
0
        wtr: &mut dyn Write,
117
0
        _runtime_allocation: usize,
118
0
        mut with: impl FnMut(&mut BorrowedBuffer<'_>) -> Result<(), Error>,
119
0
    ) -> Result<(), Error> {
120
        // Specialize for the common case of `W = String` or `W = Vec<u8>`.
121
        // In effect, we write directly into the excess capacity of `W`
122
        // instead of first writing into a stack array and then copying that
123
        // into `W`.
124
        //
125
        // This can provide up to a 40% improvement in runtime in some
126
        // microbenchmarks.
127
        //
128
        // SAFETY: We only ever write valid UTF-8. Namely, `BorrowedBuffer`
129
        // enforces this invariant.
130
        #[cfg(feature = "alloc")]
131
0
        if let Some(buf) = unsafe { wtr.as_mut_vec() } {
132
0
            buf.reserve(_runtime_allocation);
133
0
            return BorrowedBuffer::with_vec_spare_capacity(buf, with);
134
0
        }
135
0
        let mut buf = ArrayBuffer::<N>::default();
136
0
        let mut bbuf = buf.as_borrowed();
137
0
        with(&mut bbuf)?;
138
0
        wtr.write_str(bbuf.filled())?;
139
0
        Ok(())
140
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_writer::<18, <jiff::fmt::temporal::printer::DateTimePrinter>::print_time::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_writer::<29, <jiff::fmt::rfc2822::DateTimePrinter>::print_timestamp_rfc9110<&mut alloc::string::String>::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_writer::<31, <jiff::fmt::rfc2822::DateTimePrinter>::print_zoned<&mut alloc::string::String>::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_writer::<31, <jiff::fmt::rfc2822::DateTimePrinter>::print_timestamp<&mut alloc::string::String>::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_writer::<32, <jiff::fmt::temporal::printer::DateTimePrinter>::print_datetime::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_writer::<33, <jiff::fmt::temporal::printer::DateTimePrinter>::print_timestamp::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_writer::<38, <jiff::fmt::temporal::printer::DateTimePrinter>::print_timestamp_with_offset::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_writer::<72, <jiff::fmt::temporal::printer::DateTimePrinter>::print_zoned::{closure#1}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_writer::<13, <jiff::fmt::temporal::printer::DateTimePrinter>::print_date::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_writer::<13, <jiff::fmt::temporal::printer::DateTimePrinter>::print_iso_week_date::{closure#0}>
141
142
    /// Provides a borrowed buffer into the first 255 bytes of the spare
143
    /// capacity in the given `Vec<u8>` and updates the length on `Vec<u8>`
144
    /// after the closure completes to account for any new data written.
145
    ///
146
    /// In effect, this safely encapsulates writing into the uninitialized
147
    /// portion of a `Vec<u8>`.
148
    ///
149
    /// If the provided closure panics, then there is no guarantee that the
150
    /// `buf`'s length will be updated to reflect what has been written.
151
    /// However, it is guaranteed that the length setting will not lead to
152
    /// undefined behavior.
153
    #[cfg(feature = "alloc")]
154
    #[cfg_attr(feature = "perf-inline", inline(always))]
155
0
    pub(crate) fn with_vec_spare_capacity<T>(
156
0
        buf: &'data mut alloc::vec::Vec<u8>,
157
0
        mut with: impl FnMut(&mut BorrowedBuffer<'_>) -> T,
158
0
    ) -> T {
159
0
        let mut bbuf = BorrowedBuffer::from_vec_spare_capacity(buf);
160
0
        let returned = with(&mut bbuf);
161
0
        let new_len = bbuf.len();
162
        // SAFETY: `BorrowedBuffer::len()` always reflects the number of
163
        // bytes that have been written to. Thus, the data up to the given new
164
        // length is guaranteed to be initialized.
165
0
        unsafe {
166
0
            buf.set_len(new_len);
167
0
        }
168
0
        returned
169
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_vec_spare_capacity::<core::result::Result<(), jiff::error::Error>, <jiff::fmt::rfc2822::DateTimePrinter>::print_zoned<&mut alloc::string::String>::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_vec_spare_capacity::<core::result::Result<(), jiff::error::Error>, <jiff::fmt::rfc2822::DateTimePrinter>::print_timestamp<&mut alloc::string::String>::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_vec_spare_capacity::<core::result::Result<(), jiff::error::Error>, <jiff::fmt::rfc2822::DateTimePrinter>::print_timestamp_rfc9110<&mut alloc::string::String>::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_vec_spare_capacity::<core::result::Result<(), jiff::error::Error>, <jiff::fmt::temporal::printer::DateTimePrinter>::print_date::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_vec_spare_capacity::<core::result::Result<(), jiff::error::Error>, <jiff::fmt::temporal::printer::DateTimePrinter>::print_time::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_vec_spare_capacity::<core::result::Result<(), jiff::error::Error>, <jiff::fmt::temporal::printer::DateTimePrinter>::print_zoned::{closure#1}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_vec_spare_capacity::<core::result::Result<(), jiff::error::Error>, <jiff::fmt::temporal::printer::DateTimePrinter>::print_datetime::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_vec_spare_capacity::<core::result::Result<(), jiff::error::Error>, <jiff::fmt::temporal::printer::DateTimePrinter>::print_timestamp::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_vec_spare_capacity::<core::result::Result<(), jiff::error::Error>, <jiff::fmt::temporal::printer::DateTimePrinter>::print_iso_week_date::{closure#0}>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::with_vec_spare_capacity::<core::result::Result<(), jiff::error::Error>, <jiff::fmt::temporal::printer::DateTimePrinter>::print_timestamp_with_offset::{closure#0}>
170
171
    /// Build a borrowed buffer for writing into the spare capacity of a
172
    /// `Vec<u8>` allocation.
173
    ///
174
    /// This is limited only to the first `255` bytes of the spare capacity.
175
    #[cfg(feature = "alloc")]
176
    #[cfg_attr(feature = "perf-inline", inline(always))]
177
0
    pub(crate) fn from_vec_spare_capacity(
178
0
        vec: &'data mut alloc::vec::Vec<u8>,
179
0
    ) -> BorrowedBuffer<'data> {
180
0
        let data = vec.spare_capacity_mut();
181
0
        let len = data.len().min(MAX_CAPACITY);
182
0
        BorrowedBuffer::from(&mut data[..len])
183
0
    }
184
185
    /// Write the provided string to the available space in this buffer.
186
    ///
187
    /// # Panics
188
    ///
189
    /// When the available space is shorter than the length of the provided
190
    /// string. That is, when `capacity() - filled().len() < string.len()`.
191
    #[cfg_attr(feature = "perf-inline", inline(always))]
192
0
    pub(crate) fn write_str(&mut self, string: &str) {
193
        // SAFETY: A `&str`, `&[u8]` and `&[MaybeUninit<u8>]` all have the
194
        // same representation in memory.
195
0
        let data: &[MaybeUninit<u8>] = unsafe {
196
0
            core::slice::from_raw_parts(
197
0
                string.as_ptr().cast::<MaybeUninit<u8>>(),
198
0
                string.len(),
199
0
            )
200
        };
201
0
        self.available()
202
0
            .get_mut(..string.len())
203
0
            .expect("string data exceeds available buffer space")
204
0
            .copy_from_slice(data);
205
        // Cast here will never truncate because `BorrowedBuffer` is limited
206
        // to `u16::MAX` in total capacity. Above will panic if `string.len()`
207
        // exceeds available capacity, which can never be above total capacity.
208
        // Thus, if we're here, `string.len() <= u16::MAX` is guaranteed to
209
        // hold.
210
0
        self.filled += string.len() as u16;
211
0
    }
212
213
    /// Writes the given Unicode scalar value (as UTF-8) to this writer.
214
    ///
215
    /// # Panics
216
    ///
217
    /// When the available space is shorter than the UTF-8 encoding of the
218
    /// given Unicode scalar value (up to and including 4 bytes).
219
    #[cfg_attr(feature = "perf-inline", inline(always))]
220
0
    pub(crate) fn write_char(&mut self, ch: char) {
221
0
        self.write_str(ch.encode_utf8(&mut [0; 4]));
222
0
    }
223
224
    /// Writes the given ASCII byte to this writer.
225
    ///
226
    /// # Panics
227
    ///
228
    /// When the available space is shorter than 1 or if `byte` is not ASCII.
229
    #[cfg_attr(feature = "perf-inline", inline(always))]
230
0
    pub(crate) fn write_ascii_char(&mut self, byte: u8) {
231
0
        assert!(byte.is_ascii());
232
0
        self.available()
233
0
            .get_mut(0)
234
0
            .expect("insufficient buffer space to write one byte")
235
0
            .write(byte);
236
0
        self.filled += 1;
237
0
    }
238
239
    /// Writes the given `u8` integer to this buffer. No padding is performed.
240
    ///
241
    /// # Panics
242
    ///
243
    /// When the available space is insufficient to encode the number of
244
    /// digits in the decimal representation of `n`.
245
    #[cfg_attr(feature = "perf-inline", inline(always))]
246
0
    pub(crate) fn write_int(&mut self, n: impl Into<u64>) {
247
0
        let mut n = n.into();
248
0
        let digits = digits(n);
249
0
        let mut remaining_digits = usize::from(digits);
250
0
        let available = self
251
0
            .available()
252
0
            .get_mut(..remaining_digits)
253
0
            .expect("u8 integer digits exceeds available buffer space");
254
0
        while remaining_digits > 0 {
255
0
            remaining_digits -= 1;
256
0
            // SAFETY: The assert above guarantees that `remaining_digits` is
257
0
            // always in bounds.
258
0
            unsafe {
259
0
                available
260
0
                    .get_unchecked_mut(remaining_digits)
261
0
                    .write(b'0' + ((n % 10) as u8));
262
0
            }
263
0
            n /= 10;
264
0
        }
265
0
        self.filled += u16::from(digits);
266
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::write_int::<u8>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::write_int::<u32>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::write_int::<u16>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::write_int::<u64>
267
268
    /// Writes the given `u8` integer to this buffer using the given padding.
269
    ///
270
    /// The maximum allowed padding is `20`. Any values bigger than that are
271
    /// silently clamped to `20`.
272
    ///
273
    /// This always pads with zeroes.
274
    ///
275
    /// # Panics
276
    ///
277
    /// When the available space is insufficient to encode the number of
278
    /// digits in the decimal representation of `n`.
279
    ///
280
    /// This also panics when `pad_byte` is not ASCII.
281
    #[cfg_attr(feature = "perf-inline", inline(always))]
282
0
    pub(crate) fn write_int_pad0(&mut self, n: impl Into<u64>, pad_len: u8) {
283
0
        let mut n = n.into();
284
0
        let pad_len = pad_len.min(MAX_INTEGER_LEN);
285
0
        let digits = pad_len.max(digits(n));
286
0
        let mut remaining_digits = usize::from(digits);
287
0
        let available = self
288
0
            .available()
289
0
            .get_mut(..remaining_digits)
290
0
            .expect("u8 integer digits exceeds available buffer space");
291
0
        while remaining_digits > 0 {
292
0
            remaining_digits -= 1;
293
0
            // SAFETY: The assert above guarantees that `remaining_digits` is
294
0
            // always in bounds.
295
0
            unsafe {
296
0
                available
297
0
                    .get_unchecked_mut(remaining_digits)
298
0
                    .write(b'0' + ((n % 10) as u8));
299
0
            }
300
0
            n /= 10;
301
0
        }
302
0
        self.filled += u16::from(digits);
303
0
    }
304
305
    /// Writes the given `u8` integer to this buffer using the given padding.
306
    ///
307
    /// The maximum allowed padding is `20`. Any values bigger than that are
308
    /// silently clamped to `20`.
309
    ///
310
    /// # Panics
311
    ///
312
    /// When the available space is insufficient to encode the number of
313
    /// digits in the decimal representation of `n`.
314
    ///
315
    /// This also panics when `pad_byte` is not ASCII.
316
    #[cfg_attr(feature = "perf-inline", inline(always))]
317
0
    pub(crate) fn write_int_pad(
318
0
        &mut self,
319
0
        n: impl Into<u64>,
320
0
        pad_byte: u8,
321
0
        pad_len: u8,
322
0
    ) {
323
0
        assert!(pad_byte.is_ascii(), "padding byte must be ASCII");
324
325
0
        let mut n = n.into();
326
0
        let pad_len = pad_len.min(MAX_INTEGER_LEN);
327
0
        let digits = pad_len.max(digits(n));
328
0
        let mut remaining_digits = usize::from(digits);
329
0
        let available = self
330
0
            .available()
331
0
            .get_mut(..remaining_digits)
332
0
            .expect("u8 integer digits exceeds available buffer space");
333
0
        while remaining_digits > 0 {
334
0
            remaining_digits -= 1;
335
            // SAFETY: The assert above guarantees that `remaining_digits` is
336
            // always in bounds.
337
0
            unsafe {
338
0
                available
339
0
                    .get_unchecked_mut(remaining_digits)
340
0
                    .write(b'0' + ((n % 10) as u8));
341
0
            }
342
0
            n /= 10;
343
0
            if n == 0 {
344
0
                break;
345
0
            }
346
        }
347
0
        while remaining_digits > 0 {
348
0
            remaining_digits -= 1;
349
            // SAFETY: The assert above guarantees that `remaining_digits` is
350
            // always in bounds.
351
0
            unsafe {
352
0
                available.get_unchecked_mut(remaining_digits).write(pad_byte);
353
0
            }
354
        }
355
0
        self.filled += u16::from(digits);
356
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::write_int_pad::<u32>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::write_int_pad::<u64>
357
358
    /// Writes the given integer as a 1-digit integer.
359
    ///
360
    /// # Panics
361
    ///
362
    /// When the available space is shorter than 1 or if `n > 9`.
363
    #[cfg_attr(feature = "perf-inline", inline(always))]
364
0
    pub(crate) fn write_int1(&mut self, n: impl Into<u64>) {
365
0
        let n = n.into();
366
        // This is required for correctness. When `n > 9`, then the
367
        // `n as u8` below could result in writing an invalid UTF-8
368
        // byte. We don't mind incorrect results, but writing invalid
369
        // UTF-8 can lead to undefined behavior, and we want this API
370
        // to be sound.
371
0
        assert!(n <= 9);
372
0
        self.write_ascii_char(b'0' + (n as u8));
373
0
    }
374
375
    /// Writes the given integer as a 2-digit zero padded integer to this
376
    /// buffer.
377
    ///
378
    /// # Panics
379
    ///
380
    /// When the available space is shorter than 2 or if `n > 99`.
381
    #[cfg_attr(feature = "perf-inline", inline(always))]
382
0
    pub(crate) fn write_int_pad2(&mut self, n: impl Into<u64>) {
383
0
        let n = n.into();
384
        // This is required for correctness. When `n > 99`, then the
385
        // last `n as u8` below could result in writing an invalid UTF-8
386
        // byte. We don't mind incorrect results, but writing invalid
387
        // UTF-8 can lead to undefined behavior, and we want this API
388
        // to be sound.
389
        //
390
        // We omit the final `% 10` because it makes micro-benchmark results
391
        // worse. This panicking check has a more modest impact.
392
0
        assert!(n <= 99);
393
394
0
        let dst = self
395
0
            .available()
396
0
            .get_mut(..2)
397
0
            .expect("padded 2 digit integer exceeds available buffer space");
398
0
        let radix_offset = ((n % 100) * 2) as usize;
399
        // SAFETY: Valid because of the assertion above. And also,
400
        // `RADIX_100_ZERO` always has exactly 200 elements and
401
        // `radix_offset` is never greater than 198. (In that case,
402
        // we do access element at index 199 below as well.)
403
0
        unsafe {
404
0
            dst.get_unchecked_mut(0)
405
0
                .write(*RADIX_100_ZERO.get_unchecked(radix_offset));
406
0
            dst.get_unchecked_mut(1)
407
0
                .write(*RADIX_100_ZERO.get_unchecked(radix_offset + 1));
408
0
        }
409
0
        self.filled += 2;
410
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::write_int_pad2::<u8>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::write_int_pad2::<u64>
411
412
    /// Writes the given integer as a 2-digit space padded integer to this
413
    /// buffer.
414
    ///
415
    /// # Panics
416
    ///
417
    /// When the available space is shorter than 2 or if `n > 99`.
418
    #[cfg_attr(feature = "perf-inline", inline(always))]
419
0
    pub(crate) fn write_int_pad2_space(&mut self, n: impl Into<u64>) {
420
0
        let n = n.into();
421
        // This is required for correctness. When `n > 99`, then the
422
        // last `n as u8` below could result in writing an invalid UTF-8
423
        // byte. We don't mind incorrect results, but writing invalid
424
        // UTF-8 can lead to undefined behavior, and we want this API
425
        // to be sound.
426
        //
427
        // We omit the final `% 10` because it makes micro-benchmark results
428
        // worse. This panicking check has a more modest impact.
429
0
        assert!(n <= 99);
430
431
0
        let dst = self
432
0
            .available()
433
0
            .get_mut(..2)
434
0
            .expect("padded 2 digit integer exceeds available buffer space");
435
0
        let radix_offset = ((n % 100) * 2) as usize;
436
        // SAFETY: Valid because of the assertion above. And also,
437
        // `RADIX_100_ZERO` always has exactly 200 elements and
438
        // `radix_offset` is never greater than 198. (In that case,
439
        // we do access element at index 199 below as well.)
440
0
        unsafe {
441
0
            dst.get_unchecked_mut(0)
442
0
                .write(*RADIX_100_SPACE.get_unchecked(radix_offset));
443
0
            dst.get_unchecked_mut(1)
444
0
                .write(*RADIX_100_SPACE.get_unchecked(radix_offset + 1));
445
0
        }
446
0
        self.filled += 2;
447
0
    }
448
449
    /// Writes the given integer as a 4-digit zero padded integer to this
450
    /// buffer.
451
    ///
452
    /// # Panics
453
    ///
454
    /// When the available space is shorter than 4 or if `n > 9999`.
455
    #[cfg_attr(feature = "perf-inline", inline(always))]
456
0
    pub(crate) fn write_int_pad4(&mut self, n: impl Into<u64>) {
457
0
        let mut n = n.into();
458
        // This is required for correctness. When `n > 9999`, then the
459
        // last `n as u8` below could result in writing an invalid UTF-8
460
        // byte. We don't mind incorrect results, but writing invalid
461
        // UTF-8 can lead to undefined behavior, and we want this API
462
        // to be sound.
463
        //
464
        // We omit the final `% 10` because it makes micro-benchmark results
465
        // worse. This panicking check has a more modest impact.
466
0
        assert!(n <= 9999);
467
468
0
        let dst = self
469
0
            .available()
470
0
            .get_mut(..4)
471
0
            .expect("padded 4 digit integer exceeds available buffer space");
472
473
0
        let radix_offset = ((n % 100) * 2) as usize;
474
        // SAFETY: Valid because of the assertion above. And also,
475
        // `RADIX_100_ZERO` always has exactly 200 elements and
476
        // `radix_offset` is never greater than 198. (In that case,
477
        // we do access element at index 199 below as well.)
478
0
        unsafe {
479
0
            dst.get_unchecked_mut(2)
480
0
                .write(*RADIX_100_ZERO.get_unchecked(radix_offset));
481
0
            dst.get_unchecked_mut(3)
482
0
                .write(*RADIX_100_ZERO.get_unchecked(radix_offset + 1));
483
0
        }
484
485
0
        n /= 100;
486
0
        let radix_offset = ((n % 100) * 2) as usize;
487
        // SAFETY: Valid because of the assertion above. And also,
488
        // `RADIX_100_ZERO` always has exactly 200 elements and
489
        // `radix_offset` is never greater than 198. (In that case,
490
        // we do access element at index 199 below as well.)
491
0
        unsafe {
492
0
            dst.get_unchecked_mut(0)
493
0
                .write(*RADIX_100_ZERO.get_unchecked(radix_offset));
494
0
            dst.get_unchecked_mut(1)
495
0
                .write(*RADIX_100_ZERO.get_unchecked(radix_offset + 1));
496
0
        }
497
498
0
        self.filled += 4;
499
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::write_int_pad4::<u16>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer>::write_int_pad4::<u64>
500
501
    /// Writes `n` as a fractional component to the given `precision`.
502
    ///
503
    /// When `precision` is absent, then it is automatically detected based
504
    /// on the value of `n`.
505
    ///
506
    /// When `precision` is bigger than `9`, then it is clamped to `9`.
507
    ///
508
    /// # Panics
509
    ///
510
    /// When the available space is shorter than the number of digits required
511
    /// to write `n` as a fractional value.
512
    #[cfg_attr(feature = "perf-inline", inline(always))]
513
0
    pub(crate) fn write_fraction(
514
0
        &mut self,
515
0
        precision: Option<u8>,
516
0
        mut n: u32,
517
0
    ) {
518
0
        assert!(n <= 999_999_999);
519
0
        let mut buf = ArrayBuffer::<MAX_PRECISION>::default();
520
0
        for i in (0..MAX_PRECISION).rev() {
521
0
            unsafe {
522
0
                buf.data.get_unchecked_mut(i).write(b'0' + ((n % 10) as u8));
523
0
            }
524
0
            n /= 10;
525
0
        }
526
527
0
        let end = precision
528
0
            .map(|p| p.min(MAX_PRECISION as u8))
529
0
            .unwrap_or_else(|| {
530
                // SAFETY: The loop above is guaranteed to initialize `buf` in
531
                // its entirety.
532
0
                let buf = unsafe { buf.assume_init() };
533
0
                let mut end = MAX_PRECISION as u8;
534
0
                while end > 0 && buf[usize::from(end) - 1] == b'0' {
535
0
                    end -= 1;
536
0
                }
537
0
                end
538
0
            });
539
540
0
        let buf = &buf.data[..usize::from(end)];
541
0
        self.available()
542
0
            .get_mut(..buf.len())
543
0
            .expect("fraction exceeds available buffer space")
544
0
            .copy_from_slice(buf);
545
0
        self.filled += u16::from(end);
546
0
    }
547
548
    /// Clears this buffer so that there are no filled bytes.
549
    ///
550
    /// Note that no actual clearing of data is done, but this does lose
551
    /// track of data that has been initialized and data that hasn't been
552
    /// initialized.
553
    #[cfg_attr(feature = "perf-inline", inline(always))]
554
0
    pub(crate) fn clear(&mut self) {
555
0
        self.filled = 0;
556
0
    }
557
558
    /// Returns the filled portion of this buffer.
559
    #[cfg_attr(feature = "perf-inline", inline(always))]
560
0
    pub(crate) fn filled(&self) -> &str {
561
        // SAFETY: It is guaranteed that `..self.len()` is always a valid
562
        // range into `self.data` since `self.filled` is only increased upon
563
        // a valid write.
564
0
        let filled = unsafe { self.data.get_unchecked(..self.len()) };
565
        // SAFETY: Everything up to `self.filled` is guaranteed to be
566
        // initialized. Also, since `MaybeUninit<u8>` and `u8` have the same
567
        // representation, we can cast from `&[MaybeUninit<u8>]` to `&[u8]`.
568
        // Finally, the `BorrowedBuffer` API specifically guarantees that
569
        // all writes to `self.data` are valid UTF-8.
570
        unsafe {
571
0
            core::str::from_utf8_unchecked(core::slice::from_raw_parts(
572
0
                filled.as_ptr().cast::<u8>(),
573
0
                self.len(),
574
0
            ))
575
        }
576
0
    }
577
578
    /// Returns the available space in this buffer.
579
    #[cfg_attr(feature = "perf-inline", inline(always))]
580
0
    fn available(&mut self) -> &mut [MaybeUninit<u8>] {
581
        // SAFETY: `self.len()` is guaranteed to be a valid offset for the
582
        // start of a slice into `self.data`.
583
0
        unsafe { self.data.get_unchecked_mut(self.len()..) }
584
0
    }
585
586
    /// Return the length of the "filled" in bytes.
587
    ///
588
    /// This is always equivalent to the length of the slice returned by
589
    /// `BorrowedBuffer::filled`.
590
    #[cfg_attr(feature = "perf-inline", inline(always))]
591
0
    fn len(&self) -> usize {
592
0
        usize::from(self.filled)
593
0
    }
594
595
    /// Return the total unused capacity available to this buffer.
596
    #[cfg_attr(feature = "perf-inline", inline(always))]
597
0
    fn available_capacity(&self) -> usize {
598
0
        self.capacity() - self.len()
599
0
    }
600
601
    /// Return the total capacity available to this buffer.
602
    #[cfg_attr(feature = "perf-inline", inline(always))]
603
0
    fn capacity(&self) -> usize {
604
0
        self.data.len()
605
0
    }
606
}
607
608
/// Construct a borrowed buffer for writing into a `&mut [u8]`.
609
///
610
/// This typically isn't useful on its own since `&mut [u8]` is already
611
/// guaranteed to be initialized and doesn't require handling with
612
/// care. However, this is useful when using with APIs that expect a
613
/// `BorrowedBuffer`.
614
///
615
/// # Panics
616
///
617
/// When the slice exceeds the maximum capacity supported by `BorrowedBuffer`.
618
impl<'data> From<&'data mut [u8]> for BorrowedBuffer<'data> {
619
    #[cfg_attr(feature = "perf-inline", inline(always))]
620
0
    fn from(data: &'data mut [u8]) -> BorrowedBuffer<'data> {
621
0
        assert!(
622
0
            data.len() <= MAX_CAPACITY,
623
0
            "borrowed buffer only supports {MAX_CAPACITY} bytes"
624
        );
625
0
        let len = data.len();
626
0
        let data: *mut MaybeUninit<u8> = data.as_mut_ptr().cast();
627
        // SAFETY: The length hasn't changed and `MaybeUninit<u8>` and `u8`
628
        // are guaranteed to have the same representation in memory.
629
0
        let data = unsafe { core::slice::from_raw_parts_mut(data, len) };
630
0
        BorrowedBuffer { data, filled: 0 }
631
0
    }
632
}
633
634
/// Construct a borrowed buffer directly from a slice of uninitialized data.
635
///
636
/// # Panics
637
///
638
/// When the slice exceeds the maximum capacity supported by `BorrowedBuffer`.
639
impl<'data> From<&'data mut [MaybeUninit<u8>]> for BorrowedBuffer<'data> {
640
    #[cfg_attr(feature = "perf-inline", inline(always))]
641
0
    fn from(data: &'data mut [MaybeUninit<u8>]) -> BorrowedBuffer<'data> {
642
0
        assert!(
643
0
            data.len() <= MAX_CAPACITY,
644
0
            "borrowed buffer only supports {MAX_CAPACITY} bytes"
645
        );
646
0
        BorrowedBuffer { data, filled: 0 }
647
0
    }
648
}
649
650
/// Construct a borrowed buffer directly from an array of uninitialized data.
651
///
652
/// # Panics
653
///
654
/// When the array exceeds the maximum capacity supported by `BorrowedBuffer`.
655
impl<'data, const N: usize> From<&'data mut [MaybeUninit<u8>; N]>
656
    for BorrowedBuffer<'data>
657
{
658
    #[cfg_attr(feature = "perf-inline", inline(always))]
659
0
    fn from(data: &'data mut [MaybeUninit<u8>; N]) -> BorrowedBuffer<'data> {
660
0
        BorrowedBuffer::from(&mut data[..])
661
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 19]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 35]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 73]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 78]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 9]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 100]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 306]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 190]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 194]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 18]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 29]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 31]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 32]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 33]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 38]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 72]>>::from
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedBuffer as core::convert::From<&mut [core::mem::maybe_uninit::MaybeUninit<u8>; 13]>>::from
662
}
663
664
/// A buffering abstraction on top of `BorrowedBuffer`.
665
///
666
/// This lets callers make use of a monomorphic uninitialized buffer while
667
/// writing variable length data. For example, in use with `strftime`, where
668
/// the length of the resulting string can be arbitrarily long.
669
///
670
/// Essentially, once the buffer is filled up, it is emptied by writing it
671
/// to an underlying `jiff::fmt::Write` implementation.
672
///
673
/// # Design
674
///
675
/// We specifically do not expose the underlying `BorrowedBuffer` in this API.
676
/// It is too error prone because it makes it ridiculously easy for the caller
677
/// to try to write too much data to the buffer, thus causing a panic.
678
///
679
/// Also, we require that the total capacity of the `BorrowedBuffer` given
680
/// is big enough such that any of the integer formatting routines will always
681
/// fit. This means we don't need to break up integer formatting to support
682
/// pathologically small buffer sizes, e.g., 0 or 1 bytes. This is fine because
683
/// this is a Jiff-internal abstraction.
684
///
685
/// Callers must call `BorrowedWriter::finish` when done to ensure the internal
686
/// buffer is properly flushed.
687
///
688
/// One somewhat unfortunate aspect of the design here is that the integer
689
/// formatting routines need to know how much data is going to be written. This
690
/// sometimes requires doing some work to figure out. And that work is usually
691
/// repeated by `BorrowedBuffer`. My hope at time of writing (2026-01-02) is
692
/// that compiler eliminates the duplication, but I haven't actually checked
693
/// this yet.
694
///
695
/// `BorrowedWriter::write_str` is the only method where there is some
696
/// awareness of the underlying `Write` implementation. This is because the
697
/// string can be of arbitrary length, and thus, may exceed the size of
698
/// the buffer. (In which case, we pass it through directly to the `Write`
699
/// implementation.)
700
pub(crate) struct BorrowedWriter<'buffer, 'data, 'write> {
701
    bbuf: &'buffer mut BorrowedBuffer<'data>,
702
    wtr: &'write mut dyn Write,
703
}
704
705
impl<'buffer, 'data, 'write> BorrowedWriter<'buffer, 'data, 'write> {
706
    /// Creates a new borrowed writer that buffers writes in `bbuf` and flushes
707
    /// them to `wtr`.
708
    ///
709
    /// # Panics
710
    ///
711
    /// When `BorrowedBuffer` is too small to handle formatting a single
712
    /// integer (including padding).
713
    #[cfg_attr(feature = "perf-inline", inline(always))]
714
0
    pub(crate) fn new(
715
0
        bbuf: &'buffer mut BorrowedBuffer<'data>,
716
0
        wtr: &'write mut dyn Write,
717
0
    ) -> BorrowedWriter<'buffer, 'data, 'write> {
718
0
        assert!(bbuf.capacity() >= BROAD_MINIMUM_BUFFER_LEN);
719
0
        BorrowedWriter { bbuf, wtr }
720
0
    }
721
722
    #[cfg_attr(feature = "perf-inline", inline(always))]
723
0
    pub(crate) fn finish(self) -> Result<(), Error> {
724
0
        self.wtr.write_str(self.bbuf.filled())?;
725
0
        self.bbuf.clear();
726
0
        Ok(())
727
0
    }
728
729
    #[cold]
730
    #[inline(never)]
731
0
    pub(crate) fn flush(&mut self) -> Result<(), Error> {
732
0
        self.wtr.write_str(self.bbuf.filled())?;
733
0
        self.bbuf.clear();
734
0
        Ok(())
735
0
    }
736
737
    #[cfg_attr(feature = "perf-inline", inline(always))]
738
0
    pub(crate) fn if_will_fill_then_flush(
739
0
        &mut self,
740
0
        additional: impl Into<usize>,
741
0
    ) -> Result<(), Error> {
742
0
        let n = additional.into();
743
0
        if self.bbuf.len().saturating_add(n) > self.bbuf.capacity() {
744
0
            self.flush()?;
745
0
        }
746
0
        Ok(())
747
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedWriter>::if_will_fill_then_flush::<u8>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedWriter>::if_will_fill_then_flush::<usize>
748
749
    #[cfg_attr(feature = "perf-inline", inline(always))]
750
0
    pub(crate) fn write_str(&mut self, string: &str) -> Result<(), Error> {
751
0
        if string.len() > self.bbuf.available_capacity() {
752
0
            self.flush()?;
753
0
            if string.len() > self.bbuf.available_capacity() {
754
0
                return self.wtr.write_str(string);
755
0
            }
756
0
        }
757
0
        self.bbuf.write_str(string);
758
0
        Ok(())
759
0
    }
760
761
    #[cfg_attr(feature = "perf-inline", inline(always))]
762
0
    pub(crate) fn write_char(&mut self, ch: char) -> Result<(), Error> {
763
0
        self.if_will_fill_then_flush(ch.len_utf8())?;
764
0
        self.bbuf.write_char(ch);
765
0
        Ok(())
766
0
    }
767
768
    #[cfg_attr(feature = "perf-inline", inline(always))]
769
0
    pub(crate) fn write_ascii_char(&mut self, byte: u8) -> Result<(), Error> {
770
0
        if self.bbuf.available_capacity() == 0 {
771
0
            self.flush()?;
772
0
        }
773
0
        self.bbuf.write_ascii_char(byte);
774
0
        Ok(())
775
0
    }
776
777
    #[cfg_attr(feature = "perf-inline", inline(always))]
778
0
    pub(crate) fn write_int_pad(
779
0
        &mut self,
780
0
        n: impl Into<u64>,
781
0
        pad_byte: u8,
782
0
        pad_len: u8,
783
0
    ) -> Result<(), Error> {
784
0
        let n = n.into();
785
0
        let pad_len = pad_len.min(MAX_INTEGER_LEN);
786
0
        let digits = pad_len.max(digits(n));
787
0
        self.if_will_fill_then_flush(digits)?;
788
0
        self.bbuf.write_int_pad(n, pad_byte, pad_len);
789
0
        Ok(())
790
0
    }
791
792
    #[cfg_attr(feature = "perf-inline", inline(always))]
793
0
    pub(crate) fn write_int_pad2(
794
0
        &mut self,
795
0
        n: impl Into<u64>,
796
0
    ) -> Result<(), Error> {
797
0
        self.if_will_fill_then_flush(2usize)?;
798
0
        self.bbuf.write_int_pad2(n);
799
0
        Ok(())
800
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedWriter>::write_int_pad2::<u8>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedWriter>::write_int_pad2::<u64>
801
802
    #[cfg_attr(feature = "perf-inline", inline(always))]
803
0
    pub(crate) fn write_int_pad2_space(
804
0
        &mut self,
805
0
        n: impl Into<u64>,
806
0
    ) -> Result<(), Error> {
807
0
        self.if_will_fill_then_flush(2usize)?;
808
0
        self.bbuf.write_int_pad2_space(n);
809
0
        Ok(())
810
0
    }
811
812
    #[cfg_attr(feature = "perf-inline", inline(always))]
813
0
    pub(crate) fn write_int_pad4(
814
0
        &mut self,
815
0
        n: impl Into<u64>,
816
0
    ) -> Result<(), Error> {
817
0
        self.if_will_fill_then_flush(4usize)?;
818
0
        self.bbuf.write_int_pad4(n);
819
0
        Ok(())
820
0
    }
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedWriter>::write_int_pad4::<u16>
Unexecuted instantiation: <jiff::fmt::buffer::BorrowedWriter>::write_int_pad4::<u64>
821
822
    #[cfg_attr(feature = "perf-inline", inline(always))]
823
0
    pub(crate) fn write_fraction(
824
0
        &mut self,
825
0
        precision: Option<u8>,
826
0
        n: u32,
827
0
    ) -> Result<(), Error> {
828
        // It's hard to know up front how many digits we're going to print
829
        // without doing the work required to print the digits. So we just
830
        // assume this will always write 9 digits when called. We could do
831
        // a little better here when `precision` is not `None`, but I'm not
832
        // clear if it's worth it or not. I think in practice, for common
833
        // cases, our uninit buffer will be big enough anyway even when we're
834
        // pessimistic about the number of digits we'll print.
835
0
        self.if_will_fill_then_flush(9usize)?;
836
0
        self.bbuf.write_fraction(precision, n);
837
0
        Ok(())
838
0
    }
839
}
840
841
/// We come full circle and make a `BorrowedWriter` implement
842
/// `jiff::fmt::Write`.
843
///
844
/// This is concretely useful for `strftime` and passing a borrowed writer
845
/// to methods on the `Custom` trait.
846
impl<'buffer, 'data, 'write> Write for BorrowedWriter<'buffer, 'data, 'write> {
847
0
    fn write_str(&mut self, string: &str) -> Result<(), Error> {
848
0
        BorrowedWriter::write_str(self, string)
849
0
    }
850
}
851
852
/// Returns the number of digits in the decimal representation of `n`.
853
///
854
/// This calculation to figure out the number of digits to write in `n` is
855
/// the expense we incur by having our printers write forwards. If we instead
856
/// wrote backwards, then we could omit this calculation. I ended up choosing
857
/// this design because 1) most integer writes in datetime (not span) printing
858
/// are fixed 2 or 4 digits, and don't require this extra computation and 2)
859
/// writing backwards just overall seems like a pain.
860
#[cfg_attr(feature = "perf-inline", inline(always))]
861
0
fn digits(n: u64) -> u8 {
862
    // It's faster by about 1-5% (on microbenchmarks) to make this more
863
    // branch-y and specialize the smaller and more common integers in lieu
864
    // of calling `ilog10`.
865
0
    match n {
866
0
        0..=9 => 1,
867
0
        10..=99 => 2,
868
0
        100..=999 => 3,
869
0
        1000..=9999 => 4,
870
0
        _ => n.ilog10() as u8 + 1,
871
    }
872
0
}
873
874
#[cfg(test)]
875
mod tests {
876
    use super::*;
877
878
    #[test]
879
    fn write_str_array() {
880
        let mut buf = ArrayBuffer::<100>::default();
881
        let mut bbuf = buf.as_borrowed();
882
        bbuf.write_str("Hello, world!");
883
        assert_eq!(bbuf.filled(), "Hello, world!");
884
        let buf = bbuf.filled();
885
        assert_eq!(buf, "Hello, world!");
886
    }
887
888
    #[test]
889
    fn write_int_array() {
890
        let mut buf = ArrayBuffer::<100>::default();
891
        let mut bbuf = buf.as_borrowed();
892
893
        bbuf.write_int(0u64);
894
        {
895
            let buf = bbuf.filled();
896
            assert_eq!(buf, "0");
897
        }
898
899
        bbuf.clear();
900
        bbuf.write_int(1u64);
901
        {
902
            let buf = bbuf.filled();
903
            assert_eq!(buf, "1");
904
        }
905
906
        bbuf.clear();
907
        bbuf.write_int(10u64);
908
        {
909
            let buf = bbuf.filled();
910
            assert_eq!(buf, "10");
911
        }
912
913
        bbuf.clear();
914
        bbuf.write_int(100u64);
915
        {
916
            let buf = bbuf.filled();
917
            assert_eq!(buf, "100");
918
        }
919
920
        bbuf.clear();
921
        bbuf.write_int(u64::MAX);
922
        {
923
            let buf = bbuf.filled();
924
            assert_eq!(buf, "18446744073709551615");
925
        }
926
    }
927
928
    #[test]
929
    fn write_int_pad2() {
930
        let mut buf = ArrayBuffer::<100>::default();
931
        let mut bbuf = buf.as_borrowed();
932
933
        bbuf.write_int_pad2(0u64);
934
        {
935
            let buf = bbuf.filled();
936
            assert_eq!(buf, "00");
937
        }
938
939
        bbuf.clear();
940
        bbuf.write_int_pad2(1u64);
941
        {
942
            let buf = bbuf.filled();
943
            assert_eq!(buf, "01");
944
        }
945
946
        bbuf.clear();
947
        bbuf.write_int_pad2(10u64);
948
        {
949
            let buf = bbuf.filled();
950
            assert_eq!(buf, "10");
951
        }
952
953
        bbuf.clear();
954
        bbuf.write_int_pad2(99u64);
955
        {
956
            let buf = bbuf.filled();
957
            assert_eq!(buf, "99");
958
        }
959
    }
960
961
    #[test]
962
    #[should_panic]
963
    fn write_int_pad2_panic() {
964
        let mut buf = ArrayBuffer::<100>::default();
965
        let mut bbuf = buf.as_borrowed();
966
        // technically unspecified behavior,
967
        // but should not result in undefined behavior.
968
        bbuf.write_int_pad2(u64::MAX);
969
    }
970
971
    #[test]
972
    fn write_int_pad4() {
973
        let mut buf = ArrayBuffer::<100>::default();
974
        let mut bbuf = buf.as_borrowed();
975
976
        bbuf.write_int_pad4(0u64);
977
        {
978
            let buf = bbuf.filled();
979
            assert_eq!(buf, "0000");
980
        }
981
982
        bbuf.clear();
983
        bbuf.write_int_pad4(1u64);
984
        {
985
            let buf = bbuf.filled();
986
            assert_eq!(buf, "0001");
987
        }
988
989
        bbuf.clear();
990
        bbuf.write_int_pad4(10u64);
991
        {
992
            let buf = bbuf.filled();
993
            assert_eq!(buf, "0010");
994
        }
995
996
        bbuf.clear();
997
        bbuf.write_int_pad4(99u64);
998
        {
999
            let buf = bbuf.filled();
1000
            assert_eq!(buf, "0099");
1001
        }
1002
1003
        bbuf.clear();
1004
        bbuf.write_int_pad4(999u64);
1005
        {
1006
            let buf = bbuf.filled();
1007
            assert_eq!(buf, "0999");
1008
        }
1009
1010
        bbuf.clear();
1011
        bbuf.write_int_pad4(9999u64);
1012
        {
1013
            let buf = bbuf.filled();
1014
            assert_eq!(buf, "9999");
1015
        }
1016
    }
1017
1018
    #[test]
1019
    #[should_panic]
1020
    fn write_int_pad4_panic() {
1021
        let mut buf = ArrayBuffer::<100>::default();
1022
        let mut bbuf = buf.as_borrowed();
1023
        // technically unspecified behavior,
1024
        // but should not result in undefined behavior.
1025
        bbuf.write_int_pad4(u64::MAX);
1026
    }
1027
1028
    #[test]
1029
    fn write_int_pad_zero() {
1030
        let mut buf = ArrayBuffer::<100>::default();
1031
        let mut bbuf = buf.as_borrowed();
1032
1033
        bbuf.write_int_pad(0u64, b'0', 0);
1034
        {
1035
            let buf = bbuf.filled();
1036
            assert_eq!(buf, "0");
1037
        }
1038
1039
        bbuf.clear();
1040
        bbuf.write_int_pad(0u64, b'0', 1);
1041
        {
1042
            let buf = bbuf.filled();
1043
            assert_eq!(buf, "0");
1044
        }
1045
1046
        bbuf.clear();
1047
        bbuf.write_int_pad(0u64, b'0', 2);
1048
        {
1049
            let buf = bbuf.filled();
1050
            assert_eq!(buf, "00");
1051
        }
1052
1053
        bbuf.clear();
1054
        bbuf.write_int_pad(0u64, b'0', 20);
1055
        {
1056
            let buf = bbuf.filled();
1057
            assert_eq!(buf, "00000000000000000000");
1058
        }
1059
1060
        bbuf.clear();
1061
        // clamped to 20
1062
        bbuf.write_int_pad(0u64, b'0', 21);
1063
        {
1064
            let buf = bbuf.filled();
1065
            assert_eq!(buf, "00000000000000000000");
1066
        }
1067
1068
        bbuf.clear();
1069
        bbuf.write_int_pad(0u64, b' ', 2);
1070
        {
1071
            let buf = bbuf.filled();
1072
            assert_eq!(buf, " 0");
1073
        }
1074
    }
1075
1076
    #[test]
1077
    fn write_int_pad_one() {
1078
        let mut buf = ArrayBuffer::<100>::default();
1079
        let mut bbuf = buf.as_borrowed();
1080
1081
        bbuf.write_int_pad(1u64, b'0', 0);
1082
        {
1083
            let buf = bbuf.filled();
1084
            assert_eq!(buf, "1");
1085
        }
1086
1087
        bbuf.clear();
1088
        bbuf.write_int_pad(1u64, b'0', 1);
1089
        {
1090
            let buf = bbuf.filled();
1091
            assert_eq!(buf, "1");
1092
        }
1093
1094
        bbuf.clear();
1095
        bbuf.write_int_pad(1u64, b'0', 2);
1096
        {
1097
            let buf = bbuf.filled();
1098
            assert_eq!(buf, "01");
1099
        }
1100
1101
        bbuf.clear();
1102
        bbuf.write_int_pad(1u64, b'0', 20);
1103
        {
1104
            let buf = bbuf.filled();
1105
            assert_eq!(buf, "00000000000000000001");
1106
        }
1107
1108
        bbuf.clear();
1109
        // clamped to 20
1110
        bbuf.write_int_pad(1u64, b'0', 21);
1111
        {
1112
            let buf = bbuf.filled();
1113
            assert_eq!(buf, "00000000000000000001");
1114
        }
1115
1116
        bbuf.clear();
1117
        bbuf.write_int_pad(1u64, b' ', 2);
1118
        {
1119
            let buf = bbuf.filled();
1120
            assert_eq!(buf, " 1");
1121
        }
1122
    }
1123
1124
    #[test]
1125
    fn write_int_pad_max() {
1126
        let mut buf = ArrayBuffer::<100>::default();
1127
        let mut bbuf = buf.as_borrowed();
1128
1129
        bbuf.write_int_pad(u64::MAX, b'0', 0);
1130
        {
1131
            let buf = bbuf.filled();
1132
            assert_eq!(buf, "18446744073709551615");
1133
        }
1134
1135
        bbuf.clear();
1136
        bbuf.write_int_pad(u64::MAX, b'0', 1);
1137
        {
1138
            let buf = bbuf.filled();
1139
            assert_eq!(buf, "18446744073709551615");
1140
        }
1141
1142
        bbuf.clear();
1143
        bbuf.write_int_pad(u64::MAX, b'0', 2);
1144
        {
1145
            let buf = bbuf.filled();
1146
            assert_eq!(buf, "18446744073709551615");
1147
        }
1148
1149
        bbuf.clear();
1150
        bbuf.write_int_pad(u64::MAX, b'0', 20);
1151
        {
1152
            let buf = bbuf.filled();
1153
            assert_eq!(buf, "18446744073709551615");
1154
        }
1155
1156
        bbuf.clear();
1157
        // clamped to 20
1158
        bbuf.write_int_pad(u64::MAX, b'0', 21);
1159
        {
1160
            let buf = bbuf.filled();
1161
            assert_eq!(buf, "18446744073709551615");
1162
        }
1163
1164
        bbuf.clear();
1165
        bbuf.write_int_pad(u64::MAX, b' ', 2);
1166
        {
1167
            let buf = bbuf.filled();
1168
            assert_eq!(buf, "18446744073709551615");
1169
        }
1170
    }
1171
1172
    #[test]
1173
    #[should_panic]
1174
    fn write_int_pad_non_ascii_panic() {
1175
        let mut buf = ArrayBuffer::<100>::default();
1176
        let mut bbuf = buf.as_borrowed();
1177
        bbuf.write_int_pad(0u64, 0xFF, 0);
1178
    }
1179
1180
    #[test]
1181
    #[should_panic]
1182
    fn write_int_pad_insufficient_capacity_panic() {
1183
        let mut buf = ArrayBuffer::<19>::default();
1184
        let mut bbuf = buf.as_borrowed();
1185
        bbuf.write_int_pad(0u64, b'0', 20);
1186
    }
1187
1188
    #[test]
1189
    fn write_fraction_no_precision() {
1190
        let mut buf = ArrayBuffer::<100>::default();
1191
        let mut bbuf = buf.as_borrowed();
1192
1193
        bbuf.write_fraction(None, 0);
1194
        {
1195
            let buf = bbuf.filled();
1196
            assert_eq!(buf, "");
1197
        }
1198
1199
        bbuf.clear();
1200
        bbuf.write_fraction(None, 1);
1201
        {
1202
            let buf = bbuf.filled();
1203
            assert_eq!(buf, "000000001");
1204
        }
1205
1206
        bbuf.clear();
1207
        bbuf.write_fraction(None, 123_000_000);
1208
        {
1209
            let buf = bbuf.filled();
1210
            assert_eq!(buf, "123");
1211
        }
1212
1213
        bbuf.clear();
1214
        bbuf.write_fraction(None, 999_999_999);
1215
        {
1216
            let buf = bbuf.filled();
1217
            assert_eq!(buf, "999999999");
1218
        }
1219
    }
1220
1221
    #[test]
1222
    fn write_fraction_precision() {
1223
        let mut buf = ArrayBuffer::<100>::default();
1224
        let mut bbuf = buf.as_borrowed();
1225
1226
        bbuf.write_fraction(Some(0), 0);
1227
        {
1228
            let buf = bbuf.filled();
1229
            assert_eq!(buf, "");
1230
        }
1231
1232
        bbuf.clear();
1233
        bbuf.write_fraction(Some(1), 0);
1234
        {
1235
            let buf = bbuf.filled();
1236
            assert_eq!(buf, "0");
1237
        }
1238
1239
        bbuf.clear();
1240
        bbuf.write_fraction(Some(9), 0);
1241
        {
1242
            let buf = bbuf.filled();
1243
            assert_eq!(buf, "000000000");
1244
        }
1245
1246
        bbuf.clear();
1247
        bbuf.write_fraction(Some(0), 1);
1248
        {
1249
            let buf = bbuf.filled();
1250
            assert_eq!(buf, "");
1251
        }
1252
1253
        bbuf.clear();
1254
        bbuf.write_fraction(Some(9), 1);
1255
        {
1256
            let buf = bbuf.filled();
1257
            assert_eq!(buf, "000000001");
1258
        }
1259
1260
        bbuf.clear();
1261
        bbuf.write_fraction(Some(0), 123_000_000);
1262
        {
1263
            let buf = bbuf.filled();
1264
            assert_eq!(buf, "");
1265
        }
1266
1267
        bbuf.clear();
1268
        bbuf.write_fraction(Some(1), 123_000_000);
1269
        {
1270
            let buf = bbuf.filled();
1271
            assert_eq!(buf, "1");
1272
        }
1273
1274
        bbuf.clear();
1275
        bbuf.write_fraction(Some(2), 123_000_000);
1276
        {
1277
            let buf = bbuf.filled();
1278
            assert_eq!(buf, "12");
1279
        }
1280
1281
        bbuf.clear();
1282
        bbuf.write_fraction(Some(3), 123_000_000);
1283
        {
1284
            let buf = bbuf.filled();
1285
            assert_eq!(buf, "123");
1286
        }
1287
1288
        bbuf.clear();
1289
        bbuf.write_fraction(Some(6), 123_000_000);
1290
        {
1291
            let buf = bbuf.filled();
1292
            assert_eq!(buf, "123000");
1293
        }
1294
1295
        bbuf.clear();
1296
        bbuf.write_fraction(Some(9), 123_000_000);
1297
        {
1298
            let buf = bbuf.filled();
1299
            assert_eq!(buf, "123000000");
1300
        }
1301
1302
        bbuf.clear();
1303
        // clamps to 9
1304
        bbuf.write_fraction(Some(10), 123_000_000);
1305
        {
1306
            let buf = bbuf.filled();
1307
            assert_eq!(buf, "123000000");
1308
        }
1309
1310
        bbuf.clear();
1311
        bbuf.write_fraction(Some(0), 999_999_999);
1312
        {
1313
            let buf = bbuf.filled();
1314
            assert_eq!(buf, "");
1315
        }
1316
1317
        bbuf.clear();
1318
        bbuf.write_fraction(Some(1), 999_999_999);
1319
        {
1320
            let buf = bbuf.filled();
1321
            assert_eq!(buf, "9");
1322
        }
1323
1324
        bbuf.clear();
1325
        bbuf.write_fraction(Some(3), 999_999_999);
1326
        {
1327
            let buf = bbuf.filled();
1328
            assert_eq!(buf, "999");
1329
        }
1330
1331
        bbuf.clear();
1332
        bbuf.write_fraction(Some(6), 999_999_999);
1333
        {
1334
            let buf = bbuf.filled();
1335
            assert_eq!(buf, "999999");
1336
        }
1337
1338
        bbuf.clear();
1339
        bbuf.write_fraction(Some(9), 999_999_999);
1340
        {
1341
            let buf = bbuf.filled();
1342
            assert_eq!(buf, "999999999");
1343
        }
1344
    }
1345
1346
    #[test]
1347
    #[should_panic]
1348
    fn write_fraction_too_big_panic() {
1349
        let mut buf = ArrayBuffer::<100>::default();
1350
        let mut bbuf = buf.as_borrowed();
1351
        bbuf.write_fraction(None, 1_000_000_000);
1352
    }
1353
}