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