Coverage Report

Created: 2026-07-25 06:05

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