use core::fmt;
use core::str::from_utf8_unchecked;
pub struct WriteTo<'a> {
buffer: &'a mut [u8],
used: usize, overflow: bool, }
impl<'a> WriteTo<'a> {
pub fn new(buffer: &'a mut [u8]) -> Self {
WriteTo {
buffer,
used: 0,
overflow: false,
}
}
pub fn written_bytes(&self) -> usize {
self.used
}
pub fn as_str(self) -> &'a str {
unsafe { from_utf8_unchecked(&self.buffer[..self.used]) }
}
}
fn is_not_first_utf8(ch: u8) -> bool {
(ch & 0xC0) == 0x80
}
fn find_closest_boundary(raw_string: &[u8], max_len: usize) -> usize {
debug_assert!(
max_len < raw_string.len(),
"find_closest_boundary precondition failed"
);
if max_len == 0 {
0 } else if !is_not_first_utf8(raw_string[max_len]) {
max_len } else {
let mut res_len = max_len;
loop {
if !is_not_first_utf8(raw_string[res_len - 1]) {
break res_len - 1;
}
res_len -= 1;
}
}
}
impl<'a> fmt::Write for WriteTo<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
if self.overflow {
return Ok(()); }
let remaining_buf = &mut self.buffer[self.used..];
let raw_s = s.as_bytes();
if remaining_buf.len() >= raw_s.len() {
remaining_buf[..raw_s.len()].copy_from_slice(raw_s);
self.used += raw_s.len();
} else {
self.overflow = true;
let boundary_size = find_closest_boundary(raw_s, remaining_buf.len());
remaining_buf[..boundary_size].copy_from_slice(&raw_s[..boundary_size]);
self.used += boundary_size;
}
Ok(())
}
}
pub fn fmt_truncate<'a>(buffer: &'a mut [u8], args: fmt::Arguments) -> &'a str {
let mut w = WriteTo::new(buffer);
match fmt::write(&mut w, args) {
Ok(_) => w.as_str(),
Err(_) => "",
}
}
#[cfg(test)]
pub mod tests {
#[test]
fn is_not_first_utf8_test() {
assert!(!super::is_not_first_utf8(0xE2));
assert!(super::is_not_first_utf8(0x82));
assert!(super::is_not_first_utf8(0xAC));
assert!(!super::is_not_first_utf8(0xF0));
assert!(super::is_not_first_utf8(0x90));
assert!(super::is_not_first_utf8(0x8D));
assert!(super::is_not_first_utf8(0x88));
assert!(!super::is_not_first_utf8(0x20));
}
#[test]
fn find_closest_boundary_test_ascii() {
let buf = b"Hello";
assert_eq!(super::find_closest_boundary(buf, 0), 0);
assert_eq!(super::find_closest_boundary(buf, 1), 1);
assert_eq!(super::find_closest_boundary(buf, 2), 2);
assert_eq!(super::find_closest_boundary(buf, 3), 3);
assert_eq!(super::find_closest_boundary(buf, 4), 4);
}
#[test]
fn find_closest_boundary_test_unicode() {
let buf = [
0xF0u8, 0x90, 0x8D, 0x88, 0xE2, 0x82, 0xAC, 0x20,
];
assert_eq!(super::find_closest_boundary(&buf, 0), 0);
assert_eq!(super::find_closest_boundary(&buf, 1), 0);
assert_eq!(super::find_closest_boundary(&buf, 2), 0);
assert_eq!(super::find_closest_boundary(&buf, 3), 0);
assert_eq!(super::find_closest_boundary(&buf, 4), 4);
assert_eq!(super::find_closest_boundary(&buf, 5), 4);
assert_eq!(super::find_closest_boundary(&buf, 6), 4);
assert_eq!(super::find_closest_boundary(&buf, 7), 7);
}
#[test]
fn format_ok() {
let mut buf = [0u8; 64];
let formatted: &str = super::fmt_truncate(&mut buf, format_args!("Hello{}", 42));
assert_eq!(formatted, "Hello42");
}
#[test]
fn format_truncate_ascii() {
let mut buf = [0u8; 4];
let formatted: &str = super::fmt_truncate(&mut buf, format_args!("Hello{}", 42));
assert_eq!(formatted, "Hell");
}
#[test]
fn format_truncate_ascii_second() {
let mut buf = [0u8; 6];
let formatted: &str = super::fmt_truncate(&mut buf, format_args!("Hello{}", 42));
assert_eq!(formatted, "Hello4");
}
#[test]
fn format_zero() {
let mut buf = [0u8; 0];
let formatted: &str = super::fmt_truncate(&mut buf, format_args!("Hello{}", 42));
assert_eq!(formatted, "");
}
#[test]
fn format_truncate_unicode() {
let mut buf = [0u8; 4];
let formatted: &str = super::fmt_truncate(&mut buf, format_args!("Add{}", "\u{20AC}"));
assert_eq!(formatted, "Add");
}
}