use core::borrow::Borrow;
use arrayvec::ArrayString;
use super::Case;
pub struct BufEncoder<const CAP: usize> {
buf: ArrayString<CAP>,
}
impl<const CAP: usize> BufEncoder<CAP> {
const _CHECK_EVEN_CAPACITY: () = [(); 1][CAP % 2];
#[inline]
pub fn new() -> Self { BufEncoder { buf: ArrayString::new() } }
#[inline]
#[track_caller]
pub fn put_byte(&mut self, byte: u8, case: Case) {
self.buf.push_str(&case.table().byte_to_hex(byte));
}
#[inline]
#[track_caller]
pub fn put_bytes<I>(&mut self, bytes: I, case: Case)
where
I: IntoIterator,
I::Item: Borrow<u8>,
{
self.put_bytes_inner(bytes.into_iter(), case)
}
#[inline]
#[track_caller]
fn put_bytes_inner<I>(&mut self, bytes: I, case: Case)
where
I: Iterator,
I::Item: Borrow<u8>,
{
if let Some(max) = bytes.size_hint().1 {
assert!(max <= self.space_remaining());
}
for byte in bytes {
self.put_byte(*byte.borrow(), case);
}
}
#[must_use = "this may write only part of the input buffer"]
#[inline]
#[track_caller]
pub fn put_bytes_min<'a>(&mut self, bytes: &'a [u8], case: Case) -> &'a [u8] {
let to_write = self.space_remaining().min(bytes.len());
self.put_bytes(&bytes[..to_write], case);
&bytes[to_write..]
}
#[inline]
pub fn is_full(&self) -> bool { self.space_remaining() == 0 }
#[inline]
pub fn as_str(&self) -> &str { &self.buf }
#[inline]
pub fn clear(&mut self) { self.buf.clear(); }
#[inline]
pub fn space_remaining(&self) -> usize { self.buf.remaining_capacity() / 2 }
pub(crate) fn put_filler(&mut self, filler: char, max_count: usize) -> usize {
let mut buf = [0; 4];
let filler = filler.encode_utf8(&mut buf);
let max_capacity = self.buf.remaining_capacity() / filler.len();
let to_write = max_capacity.min(max_count);
for _ in 0..to_write {
self.buf.push_str(filler);
}
to_write
}
}
impl<const CAP: usize> Default for BufEncoder<CAP> {
fn default() -> Self { Self::new() }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
let encoder = BufEncoder::<2>::new();
assert_eq!(encoder.as_str(), "");
assert!(!encoder.is_full());
}
#[test]
fn single_byte_exact_buf() {
let mut encoder = BufEncoder::<2>::new();
assert_eq!(encoder.space_remaining(), 1);
encoder.put_byte(42, Case::Lower);
assert_eq!(encoder.as_str(), "2a");
assert_eq!(encoder.space_remaining(), 0);
assert!(encoder.is_full());
encoder.clear();
assert_eq!(encoder.space_remaining(), 1);
assert!(!encoder.is_full());
encoder.put_byte(42, Case::Upper);
assert_eq!(encoder.as_str(), "2A");
assert_eq!(encoder.space_remaining(), 0);
assert!(encoder.is_full());
}
#[test]
fn single_byte_oversized_buf() {
let mut encoder = BufEncoder::<4>::new();
assert_eq!(encoder.space_remaining(), 2);
encoder.put_byte(42, Case::Lower);
assert_eq!(encoder.space_remaining(), 1);
assert_eq!(encoder.as_str(), "2a");
assert!(!encoder.is_full());
encoder.clear();
assert_eq!(encoder.space_remaining(), 2);
encoder.put_byte(42, Case::Upper);
assert_eq!(encoder.as_str(), "2A");
assert_eq!(encoder.space_remaining(), 1);
assert!(!encoder.is_full());
}
#[test]
fn two_bytes() {
let mut encoder = BufEncoder::<4>::new();
encoder.put_byte(42, Case::Lower);
assert_eq!(encoder.space_remaining(), 1);
encoder.put_byte(255, Case::Lower);
assert_eq!(encoder.space_remaining(), 0);
assert_eq!(encoder.as_str(), "2aff");
assert!(encoder.is_full());
encoder.clear();
assert!(!encoder.is_full());
encoder.put_byte(42, Case::Upper);
encoder.put_byte(255, Case::Upper);
assert_eq!(encoder.as_str(), "2AFF");
assert!(encoder.is_full());
}
#[test]
fn put_bytes_min() {
let mut encoder = BufEncoder::<2>::new();
let remainder = encoder.put_bytes_min(b"", Case::Lower);
assert_eq!(remainder, b"");
assert_eq!(encoder.as_str(), "");
let remainder = encoder.put_bytes_min(b"*", Case::Lower);
assert_eq!(remainder, b"");
assert_eq!(encoder.as_str(), "2a");
encoder.clear();
let remainder = encoder.put_bytes_min(&[42, 255], Case::Lower);
assert_eq!(remainder, &[255]);
assert_eq!(encoder.as_str(), "2a");
}
#[test]
fn same_as_fmt() {
use core::fmt::{self, Write};
struct Writer {
buf: [u8; 2],
pos: usize,
}
impl Writer {
fn as_str(&self) -> &str { core::str::from_utf8(&self.buf[..self.pos]).unwrap() }
}
impl Write for Writer {
fn write_str(&mut self, s: &str) -> fmt::Result {
assert!(self.pos <= 2);
if s.len() > 2 - self.pos {
Err(fmt::Error)
} else {
self.buf[self.pos..(self.pos + s.len())].copy_from_slice(s.as_bytes());
self.pos += s.len();
Ok(())
}
}
}
let mut writer = Writer { buf: [0u8; 2], pos: 0 };
let mut encoder = BufEncoder::<2>::new();
for i in 0..=255 {
write!(writer, "{:02x}", i).unwrap();
encoder.put_byte(i, Case::Lower);
assert_eq!(encoder.as_str(), writer.as_str());
writer.pos = 0;
encoder.clear();
}
for i in 0..=255 {
write!(writer, "{:02X}", i).unwrap();
encoder.put_byte(i, Case::Upper);
assert_eq!(encoder.as_str(), writer.as_str());
writer.pos = 0;
encoder.clear();
}
}
}