Skip to content

Commit b6da292

Browse files
committed
Add core::fmt::WriteCursor for formatting into a borrowed buffer.
1 parent 698fcc8 commit b6da292

File tree

4 files changed

+162
-0
lines changed

4 files changed

+162
-0
lines changed

library/alloc/src/fmt.rs

+2
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,8 @@ pub use core::fmt::Alignment;
557557
pub use core::fmt::Error;
558558
#[unstable(feature = "debug_closure_helpers", issue = "117729")]
559559
pub use core::fmt::FormatterFn;
560+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
561+
pub use core::fmt::WriteCursor;
560562
#[stable(feature = "rust1", since = "1.0.0")]
561563
pub use core::fmt::{write, Arguments};
562564
#[stable(feature = "rust1", since = "1.0.0")]

library/core/src/fmt/mod.rs

+125
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,131 @@ impl<W: Write + ?Sized> Write for &mut W {
231231
}
232232
}
233233

234+
/// A `WriteCursor` implements [`fmt::Write`] by writing to an in-memory buffer.
235+
///
236+
/// # Examples
237+
///
238+
/// ```
239+
/// #![feature(fmt_write_cursor)]
240+
/// use std::fmt::WriteCursor;
241+
///
242+
/// # fn test_write_cursor() -> std::fmt::Result {
243+
/// let mut buf = [0u8; 5];
244+
/// let mut cursor = WriteCursor::new(&mut buf);
245+
/// write!(cursor, "{}", 12345)?;
246+
/// assert_eq!(cursor.as_str(), "12345");
247+
/// assert_eq!(&buf, b"12345");
248+
/// # Ok(())
249+
/// # }
250+
/// ```
251+
#[doc(alias = "sprintf")]
252+
#[doc(alias = "snprintf")]
253+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
254+
pub struct WriteCursor<'a> {
255+
buf: &'a mut [mem::MaybeUninit<u8>],
256+
utf8_len: usize,
257+
}
258+
259+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
260+
impl Debug for WriteCursor<'_> {
261+
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
262+
f.debug_struct("WriteCursor")
263+
.field("capacity", &self.capacity())
264+
.field("written", &self.written())
265+
.finish()
266+
}
267+
}
268+
269+
impl<'a> WriteCursor<'a> {
270+
/// Creates a new cursor wrapping the provided buffer.
271+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
272+
pub fn new(buf: &'a mut [u8]) -> WriteCursor<'a> {
273+
// SAFETY: [T] and [MaybeUninit<T>] have the same layout
274+
Self::new_uninit(unsafe { mem::transmute(buf) })
275+
}
276+
277+
/// Creates a new cursor wrapping the provided uninitialized buffer.
278+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
279+
pub fn new_uninit(buf: &'a mut [mem::MaybeUninit<u8>]) -> WriteCursor<'a> {
280+
WriteCursor { buf, utf8_len: 0 }
281+
}
282+
283+
/// Returns the total capacity of the cursor's underlying buffer, in bytes.
284+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
285+
pub fn capacity(&self) -> usize {
286+
self.buf.len()
287+
}
288+
289+
/// Returns how many bytes have been written to the cursor's underlying buffer.
290+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
291+
pub fn written(&self) -> usize {
292+
self.utf8_len
293+
}
294+
295+
/// Returns the written portion of the cursor's underlying buffer as a
296+
/// byte slice.
297+
///
298+
/// The returned slice contains valid UTF-8.
299+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
300+
pub fn as_bytes(&self) -> &[u8] {
301+
let utf8 = &self.buf[..self.utf8_len];
302+
// SAFETY: The buffer is incrementally initialized by `write_str`, which
303+
// updates `utf8_len`.
304+
unsafe { mem::MaybeUninit::slice_assume_init_ref(utf8) }
305+
}
306+
307+
/// Consumes the cursor and returns the written portion of the cursor's
308+
/// underlying buffer as a byte slice.
309+
///
310+
/// The returned slice contains valid UTF-8.
311+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
312+
pub fn into_bytes(self) -> &'a [u8] {
313+
let utf8 = &self.buf[..self.utf8_len];
314+
// SAFETY: The buffer is incrementally initialized by `write_str`, which
315+
// updates `utf8_len`.
316+
unsafe { mem::MaybeUninit::slice_assume_init_ref(utf8) }
317+
}
318+
319+
/// Returns the written portion of the cursor's underlying buffer as a `&str`.
320+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
321+
pub fn as_str(&self) -> &str {
322+
// SAFETY: The buffer is only initialized by copying from `str` values.
323+
unsafe { str::from_utf8_unchecked(self.as_bytes()) }
324+
}
325+
326+
/// Consumes the cursor and returns the written portion of the cursor's
327+
/// underlying buffer as a `&str`.
328+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
329+
pub fn into_str(self) -> &'a str {
330+
// SAFETY: The buffer is only initialized by copying from `str` values.
331+
unsafe { str::from_utf8_unchecked(self.into_bytes()) }
332+
}
333+
334+
/// Allows the [`write!`] macro to write to a `WriteCursor`.
335+
///
336+
/// This method should generally not be invoked manually. It exists so that
337+
/// the `write!` macro can write to a `WriteCursor` without needing a
338+
/// `use std::fmt::Write` declaration.
339+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
340+
pub fn write_fmt(&mut self, args: Arguments<'_>) -> Result {
341+
Write::write_fmt(self, args)
342+
}
343+
}
344+
345+
#[unstable(feature = "fmt_write_cursor", issue = "none")]
346+
impl Write for WriteCursor<'_> {
347+
fn write_str(&mut self, s: &str) -> Result {
348+
let b = s.as_bytes();
349+
let avail = &mut self.buf[self.utf8_len..];
350+
if b.len() > avail.len() {
351+
return Err(Error);
352+
}
353+
mem::MaybeUninit::write_slice(&mut avail[..b.len()], b);
354+
self.utf8_len += b.len();
355+
Ok(())
356+
}
357+
}
358+
234359
/// Configuration for formatting.
235360
///
236361
/// A `Formatter` represents various options related to formatting. Users do not

library/core/tests/fmt/mod.rs

+34
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,37 @@ fn pad_integral_resets() {
4343

4444
assert_eq!(format!("{Bar:<03}"), "1 0051 ");
4545
}
46+
47+
#[test]
48+
fn write_cursor() {
49+
use core::fmt::WriteCursor;
50+
51+
let mut buf = [0u8; 5];
52+
53+
{
54+
let mut c = WriteCursor::new(&mut buf);
55+
assert!(write!(c, "{}", 12345).is_ok());
56+
assert_eq!(c.as_bytes(), b"12345");
57+
assert_eq!(c.as_str(), "12345");
58+
}
59+
60+
// Writes either fully succeed or leave the cursor unchanged. A cursor that
61+
// failed to write remains valid for smaller writes.
62+
{
63+
let mut c = WriteCursor::new(&mut buf);
64+
assert!(write!(c, "{}", 123456).is_err());
65+
assert_eq!(c.written(), 0);
66+
assert_eq!(c.as_str(), "");
67+
68+
assert!(write!(c, "{}", 1234).is_ok());
69+
assert_eq!(c.written(), 4);
70+
assert_eq!(c.as_str(), "1234");
71+
72+
assert!(write!(c, "{}", 1).is_ok());
73+
assert_eq!(c.written(), 5);
74+
assert_eq!(c.as_str(), "12341");
75+
76+
assert!(write!(c, "{}", 1).is_err());
77+
assert_eq!(c.written(), 5);
78+
}
79+
}

library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#![feature(extern_types)]
3535
#![feature(flt2dec)]
3636
#![feature(fmt_internals)]
37+
#![feature(fmt_write_cursor)]
3738
#![feature(float_minimum_maximum)]
3839
#![feature(future_join)]
3940
#![feature(generic_assert_internals)]

0 commit comments

Comments
 (0)