#![no_std]
#![warn(rust_2018_idioms)]
#![doc = include_str!("../README.md")]
use core::mem::{self, MaybeUninit};
use core::{fmt, slice, str};
pub struct DisplayBuffer<const SIZE: usize> {
buf: [MaybeUninit<u8>; SIZE],
len: u32,
}
impl<const SIZE: usize> DisplayBuffer<SIZE> {
pub const fn new() -> Self {
if SIZE > u32::MAX as usize {
panic!("SIZE has to be less then `u32::MAX`");
}
let buf = unsafe { MaybeUninit::uninit().assume_init() };
Self { buf, len: 0 }
}
pub const fn len(&self) -> usize {
self.len as usize
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub const fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(&self.buf as *const _ as *const _, self.len()) }
}
pub const fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.as_bytes()) }
}
fn unfilled(&mut self) -> &mut [MaybeUninit<u8>] {
let len = self.len();
&mut self.buf[len..]
}
}
impl<const SIZE: usize> fmt::Write for DisplayBuffer<SIZE> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let unfilled = self.unfilled();
let bytes = s.as_bytes();
let bytes_len = bytes.len();
if bytes_len > unfilled.len() {
return Err(fmt::Error);
}
let bytes: &[MaybeUninit<u8>] = unsafe { mem::transmute(bytes) };
unfilled[..bytes_len].copy_from_slice(bytes);
self.len += bytes_len as u32;
Ok(())
}
fn write_char(&mut self, c: char) -> fmt::Result {
let unfilled = self.unfilled();
let char_len = c.len_utf8();
if char_len > unfilled.len() {
return Err(fmt::Error);
}
c.encode_utf8(unsafe { mem::transmute(unfilled) });
self.len += char_len as u32;
Ok(())
}
}
#[doc(hidden)]
#[inline]
pub fn __fmt<const SIZE: usize>(
f: &mut fmt::Formatter<'_>,
args: fmt::Arguments<'_>,
) -> fmt::Result {
if f.precision().is_none() && f.width().is_none() {
return f.write_fmt(args);
}
use fmt::Write;
let mut buf = DisplayBuffer::<SIZE>::new();
buf.write_fmt(args)?;
f.pad(buf.as_str())
}
#[macro_export]
macro_rules! fmt {
($f:expr, $size:expr, $($arg:tt)*) => {
$crate::__fmt::<{ $size }>($f, format_args!($($arg)*))
};
}