jiff/fmt/
buffer.rs

1use core::mem::MaybeUninit;
2
3use crate::{fmt::Write, Error};
4
5const MAX_CAPACITY: usize = u16::MAX as usize;
6// From `u64::MAX.to_string().len()`.
7const MAX_INTEGER_LEN: u8 = 20;
8const 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`.
14const BROAD_MINIMUM_BUFFER_LEN: usize = 20;
15
16/// All integers in the range `0..=99`, zero padded.
17const 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.
29const 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)]
44pub(crate) struct ArrayBuffer<const N: usize> {
45    data: [MaybeUninit<u8>; N],
46}
47
48impl<const N: usize> ArrayBuffer<N> {
49    /// Return a writable buffer into this storage.
50    #[cfg_attr(feature = "perf-inline", inline(always))]
51    pub(crate) fn as_borrowed<'data>(&mut self) -> BorrowedBuffer<'_> {
52        BorrowedBuffer::from(&mut self.data)
53    }
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    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            *(&self.data as *const [MaybeUninit<u8>; N] as *const [u8; N])
66        }
67    }
68}
69
70/// Construct an uninitialized buffer of data of size `N`.
71impl<const N: usize> Default for ArrayBuffer<N> {
72    #[cfg_attr(feature = "perf-inline", inline(always))]
73    fn default() -> ArrayBuffer<N> {
74        ArrayBuffer { data: [MaybeUninit::uninit(); N] }
75    }
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)]
102pub(crate) struct BorrowedBuffer<'data> {
103    data: &'data mut [MaybeUninit<u8>],
104    filled: u16,
105}
106
107impl<'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    pub(crate) fn with_writer<const N: usize>(
116        wtr: &mut dyn Write,
117        _runtime_allocation: usize,
118        mut with: impl FnMut(&mut BorrowedBuffer<'_>) -> Result<(), Error>,
119    ) -> 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        if let Some(buf) = unsafe { wtr.as_mut_vec() } {
132            buf.reserve(_runtime_allocation);
133            return BorrowedBuffer::with_vec_spare_capacity(buf, with);
134        }
135        let mut buf = ArrayBuffer::<N>::default();
136        let mut bbuf = buf.as_borrowed();
137        with(&mut bbuf)?;
138        wtr.write_str(bbuf.filled())?;
139        Ok(())
140    }
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    pub(crate) fn with_vec_spare_capacity<T>(
156        buf: &'data mut alloc::vec::Vec<u8>,
157        mut with: impl FnMut(&mut BorrowedBuffer<'_>) -> T,
158    ) -> T {
159        let mut bbuf = BorrowedBuffer::from_vec_spare_capacity(buf);
160        let returned = with(&mut bbuf);
161        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        unsafe {
166            buf.set_len(new_len);
167        }
168        returned
169    }
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    pub(crate) fn from_vec_spare_capacity(
178        vec: &'data mut alloc::vec::Vec<u8>,
179    ) -> BorrowedBuffer<'data> {
180        let data = vec.spare_capacity_mut();
181        let len = data.len().min(MAX_CAPACITY);
182        BorrowedBuffer::from(&mut data[..len])
183    }
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    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        let data: &[MaybeUninit<u8>] = unsafe {
196            core::slice::from_raw_parts(
197                string.as_ptr().cast::<MaybeUninit<u8>>(),
198                string.len(),
199            )
200        };
201        self.available()
202            .get_mut(..string.len())
203            .expect("string data exceeds available buffer space")
204            .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        self.filled += string.len() as u16;
211    }
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    pub(crate) fn write_char(&mut self, ch: char) {
221        self.write_str(ch.encode_utf8(&mut [0; 4]));
222    }
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    pub(crate) fn write_ascii_char(&mut self, byte: u8) {
231        assert!(byte.is_ascii());
232        self.available()
233            .get_mut(0)
234            .expect("insufficient buffer space to write one byte")
235            .write(byte);
236        self.filled += 1;
237    }
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    pub(crate) fn write_int(&mut self, n: impl Into<u64>) {
247        let mut n = n.into();
248        let digits = digits(n);
249        let mut remaining_digits = usize::from(digits);
250        let available = self
251            .available()
252            .get_mut(..remaining_digits)
253            .expect("u8 integer digits exceeds available buffer space");
254        while remaining_digits > 0 {
255            remaining_digits -= 1;
256            // SAFETY: The assert above guarantees that `remaining_digits` is
257            // always in bounds.
258            unsafe {
259                available
260                    .get_unchecked_mut(remaining_digits)
261                    .write(b'0' + ((n % 10) as u8));
262            }
263            n /= 10;
264        }
265        self.filled += u16::from(digits);
266    }
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    pub(crate) fn write_int_pad0(&mut self, n: impl Into<u64>, pad_len: u8) {
283        let mut n = n.into();
284        let pad_len = pad_len.min(MAX_INTEGER_LEN);
285        let digits = pad_len.max(digits(n));
286        let mut remaining_digits = usize::from(digits);
287        let available = self
288            .available()
289            .get_mut(..remaining_digits)
290            .expect("u8 integer digits exceeds available buffer space");
291        while remaining_digits > 0 {
292            remaining_digits -= 1;
293            // SAFETY: The assert above guarantees that `remaining_digits` is
294            // always in bounds.
295            unsafe {
296                available
297                    .get_unchecked_mut(remaining_digits)
298                    .write(b'0' + ((n % 10) as u8));
299            }
300            n /= 10;
301        }
302        self.filled += u16::from(digits);
303    }
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    pub(crate) fn write_int_pad(
318        &mut self,
319        n: impl Into<u64>,
320        pad_byte: u8,
321        pad_len: u8,
322    ) {
323        assert!(pad_byte.is_ascii(), "padding byte must be ASCII");
324
325        let mut n = n.into();
326        let pad_len = pad_len.min(MAX_INTEGER_LEN);
327        let digits = pad_len.max(digits(n));
328        let mut remaining_digits = usize::from(digits);
329        let available = self
330            .available()
331            .get_mut(..remaining_digits)
332            .expect("u8 integer digits exceeds available buffer space");
333        while remaining_digits > 0 {
334            remaining_digits -= 1;
335            // SAFETY: The assert above guarantees that `remaining_digits` is
336            // always in bounds.
337            unsafe {
338                available
339                    .get_unchecked_mut(remaining_digits)
340                    .write(b'0' + ((n % 10) as u8));
341            }
342            n /= 10;
343            if n == 0 {
344                break;
345            }
346        }
347        while remaining_digits > 0 {
348            remaining_digits -= 1;
349            // SAFETY: The assert above guarantees that `remaining_digits` is
350            // always in bounds.
351            unsafe {
352                available.get_unchecked_mut(remaining_digits).write(pad_byte);
353            }
354        }
355        self.filled += u16::from(digits);
356    }
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    pub(crate) fn write_int1(&mut self, n: impl Into<u64>) {
365        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        assert!(n <= 9);
372        self.write_ascii_char(b'0' + (n as u8));
373    }
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    pub(crate) fn write_int_pad2(&mut self, n: impl Into<u64>) {
383        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        assert!(n <= 99);
393
394        let dst = self
395            .available()
396            .get_mut(..2)
397            .expect("padded 2 digit integer exceeds available buffer space");
398        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        unsafe {
404            dst.get_unchecked_mut(0)
405                .write(*RADIX_100_ZERO.get_unchecked(radix_offset));
406            dst.get_unchecked_mut(1)
407                .write(*RADIX_100_ZERO.get_unchecked(radix_offset + 1));
408        }
409        self.filled += 2;
410    }
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    pub(crate) fn write_int_pad2_space(&mut self, n: impl Into<u64>) {
420        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        assert!(n <= 99);
430
431        let dst = self
432            .available()
433            .get_mut(..2)
434            .expect("padded 2 digit integer exceeds available buffer space");
435        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        unsafe {
441            dst.get_unchecked_mut(0)
442                .write(*RADIX_100_SPACE.get_unchecked(radix_offset));
443            dst.get_unchecked_mut(1)
444                .write(*RADIX_100_SPACE.get_unchecked(radix_offset + 1));
445        }
446        self.filled += 2;
447    }
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    pub(crate) fn write_int_pad4(&mut self, n: impl Into<u64>) {
457        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        assert!(n <= 9999);
467
468        let dst = self
469            .available()
470            .get_mut(..4)
471            .expect("padded 4 digit integer exceeds available buffer space");
472
473        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        unsafe {
479            dst.get_unchecked_mut(2)
480                .write(*RADIX_100_ZERO.get_unchecked(radix_offset));
481            dst.get_unchecked_mut(3)
482                .write(*RADIX_100_ZERO.get_unchecked(radix_offset + 1));
483        }
484
485        n /= 100;
486        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        unsafe {
492            dst.get_unchecked_mut(0)
493                .write(*RADIX_100_ZERO.get_unchecked(radix_offset));
494            dst.get_unchecked_mut(1)
495                .write(*RADIX_100_ZERO.get_unchecked(radix_offset + 1));
496        }
497
498        self.filled += 4;
499    }
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    pub(crate) fn write_fraction(
514        &mut self,
515        precision: Option<u8>,
516        mut n: u32,
517    ) {
518        assert!(n <= 999_999_999);
519        let mut buf = ArrayBuffer::<MAX_PRECISION>::default();
520        for i in (0..MAX_PRECISION).rev() {
521            unsafe {
522                buf.data.get_unchecked_mut(i).write(b'0' + ((n % 10) as u8));
523            }
524            n /= 10;
525        }
526
527        let end = precision
528            .map(|p| p.min(MAX_PRECISION as u8))
529            .unwrap_or_else(|| {
530                // SAFETY: The loop above is guaranteed to initialize `buf` in
531                // its entirety.
532                let buf = unsafe { buf.assume_init() };
533                let mut end = MAX_PRECISION as u8;
534                while end > 0 && buf[usize::from(end) - 1] == b'0' {
535                    end -= 1;
536                }
537                end
538            });
539
540        let buf = &buf.data[..usize::from(end)];
541        self.available()
542            .get_mut(..buf.len())
543            .expect("fraction exceeds available buffer space")
544            .copy_from_slice(buf);
545        self.filled += u16::from(end);
546    }
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    pub(crate) fn clear(&mut self) {
555        self.filled = 0;
556    }
557
558    /// Returns the filled portion of this buffer.
559    #[cfg_attr(feature = "perf-inline", inline(always))]
560    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        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            core::str::from_utf8_unchecked(core::slice::from_raw_parts(
572                filled.as_ptr().cast::<u8>(),
573                self.len(),
574            ))
575        }
576    }
577
578    /// Returns the available space in this buffer.
579    #[cfg_attr(feature = "perf-inline", inline(always))]
580    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        unsafe { self.data.get_unchecked_mut(self.len()..) }
584    }
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    fn len(&self) -> usize {
592        usize::from(self.filled)
593    }
594
595    /// Return the total unused capacity available to this buffer.
596    #[cfg_attr(feature = "perf-inline", inline(always))]
597    fn available_capacity(&self) -> usize {
598        self.capacity() - self.len()
599    }
600
601    /// Return the total capacity available to this buffer.
602    #[cfg_attr(feature = "perf-inline", inline(always))]
603    fn capacity(&self) -> usize {
604        self.data.len()
605    }
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`.
618impl<'data> From<&'data mut [u8]> for BorrowedBuffer<'data> {
619    #[cfg_attr(feature = "perf-inline", inline(always))]
620    fn from(data: &'data mut [u8]) -> BorrowedBuffer<'data> {
621        assert!(
622            data.len() <= MAX_CAPACITY,
623            "borrowed buffer only supports {MAX_CAPACITY} bytes"
624        );
625        let len = data.len();
626        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        let data = unsafe { core::slice::from_raw_parts_mut(data, len) };
630        BorrowedBuffer { data, filled: 0 }
631    }
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`.
639impl<'data> From<&'data mut [MaybeUninit<u8>]> for BorrowedBuffer<'data> {
640    #[cfg_attr(feature = "perf-inline", inline(always))]
641    fn from(data: &'data mut [MaybeUninit<u8>]) -> BorrowedBuffer<'data> {
642        assert!(
643            data.len() <= MAX_CAPACITY,
644            "borrowed buffer only supports {MAX_CAPACITY} bytes"
645        );
646        BorrowedBuffer { data, filled: 0 }
647    }
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`.
655impl<'data, const N: usize> From<&'data mut [MaybeUninit<u8>; N]>
656    for BorrowedBuffer<'data>
657{
658    #[cfg_attr(feature = "perf-inline", inline(always))]
659    fn from(data: &'data mut [MaybeUninit<u8>; N]) -> BorrowedBuffer<'data> {
660        BorrowedBuffer::from(&mut data[..])
661    }
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.)
700pub(crate) struct BorrowedWriter<'buffer, 'data, 'write> {
701    bbuf: &'buffer mut BorrowedBuffer<'data>,
702    wtr: &'write mut dyn Write,
703}
704
705impl<'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    pub(crate) fn new(
715        bbuf: &'buffer mut BorrowedBuffer<'data>,
716        wtr: &'write mut dyn Write,
717    ) -> BorrowedWriter<'buffer, 'data, 'write> {
718        assert!(bbuf.capacity() >= BROAD_MINIMUM_BUFFER_LEN);
719        BorrowedWriter { bbuf, wtr }
720    }
721
722    #[cfg_attr(feature = "perf-inline", inline(always))]
723    pub(crate) fn finish(self) -> Result<(), Error> {
724        self.wtr.write_str(self.bbuf.filled())?;
725        self.bbuf.clear();
726        Ok(())
727    }
728
729    #[cold]
730    #[inline(never)]
731    pub(crate) fn flush(&mut self) -> Result<(), Error> {
732        self.wtr.write_str(self.bbuf.filled())?;
733        self.bbuf.clear();
734        Ok(())
735    }
736
737    #[cfg_attr(feature = "perf-inline", inline(always))]
738    pub(crate) fn if_will_fill_then_flush(
739        &mut self,
740        additional: impl Into<usize>,
741    ) -> Result<(), Error> {
742        let n = additional.into();
743        if self.bbuf.len().saturating_add(n) > self.bbuf.capacity() {
744            self.flush()?;
745        }
746        Ok(())
747    }
748
749    #[cfg_attr(feature = "perf-inline", inline(always))]
750    pub(crate) fn write_str(&mut self, string: &str) -> Result<(), Error> {
751        if string.len() > self.bbuf.available_capacity() {
752            self.flush()?;
753            if string.len() > self.bbuf.available_capacity() {
754                return self.wtr.write_str(string);
755            }
756        }
757        self.bbuf.write_str(string);
758        Ok(())
759    }
760
761    #[cfg_attr(feature = "perf-inline", inline(always))]
762    pub(crate) fn write_char(&mut self, ch: char) -> Result<(), Error> {
763        self.if_will_fill_then_flush(ch.len_utf8())?;
764        self.bbuf.write_char(ch);
765        Ok(())
766    }
767
768    #[cfg_attr(feature = "perf-inline", inline(always))]
769    pub(crate) fn write_ascii_char(&mut self, byte: u8) -> Result<(), Error> {
770        if self.bbuf.available_capacity() == 0 {
771            self.flush()?;
772        }
773        self.bbuf.write_ascii_char(byte);
774        Ok(())
775    }
776
777    #[cfg_attr(feature = "perf-inline", inline(always))]
778    pub(crate) fn write_int_pad(
779        &mut self,
780        n: impl Into<u64>,
781        pad_byte: u8,
782        pad_len: u8,
783    ) -> Result<(), Error> {
784        let n = n.into();
785        let pad_len = pad_len.min(MAX_INTEGER_LEN);
786        let digits = pad_len.max(digits(n));
787        self.if_will_fill_then_flush(digits)?;
788        self.bbuf.write_int_pad(n, pad_byte, pad_len);
789        Ok(())
790    }
791
792    #[cfg_attr(feature = "perf-inline", inline(always))]
793    pub(crate) fn write_int_pad2(
794        &mut self,
795        n: impl Into<u64>,
796    ) -> Result<(), Error> {
797        self.if_will_fill_then_flush(2usize)?;
798        self.bbuf.write_int_pad2(n);
799        Ok(())
800    }
801
802    #[cfg_attr(feature = "perf-inline", inline(always))]
803    pub(crate) fn write_int_pad2_space(
804        &mut self,
805        n: impl Into<u64>,
806    ) -> Result<(), Error> {
807        self.if_will_fill_then_flush(2usize)?;
808        self.bbuf.write_int_pad2_space(n);
809        Ok(())
810    }
811
812    #[cfg_attr(feature = "perf-inline", inline(always))]
813    pub(crate) fn write_int_pad4(
814        &mut self,
815        n: impl Into<u64>,
816    ) -> Result<(), Error> {
817        self.if_will_fill_then_flush(4usize)?;
818        self.bbuf.write_int_pad4(n);
819        Ok(())
820    }
821
822    #[cfg_attr(feature = "perf-inline", inline(always))]
823    pub(crate) fn write_fraction(
824        &mut self,
825        precision: Option<u8>,
826        n: u32,
827    ) -> 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        self.if_will_fill_then_flush(9usize)?;
836        self.bbuf.write_fraction(precision, n);
837        Ok(())
838    }
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.
846impl<'buffer, 'data, 'write> Write for BorrowedWriter<'buffer, 'data, 'write> {
847    fn write_str(&mut self, string: &str) -> Result<(), Error> {
848        BorrowedWriter::write_str(self, string)
849    }
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))]
861fn 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    match n {
866        0..=9 => 1,
867        10..=99 => 2,
868        100..=999 => 3,
869        1000..=9999 => 4,
870        _ => n.ilog10() as u8 + 1,
871    }
872}
873
874#[cfg(test)]
875mod 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}