use core::ops::{Bound, RangeBounds};
use crate::{error::TryAdvanceError, EmptyChunk};
use super::{
super::{must_non_zero, panic_advance},
ChunkMut, ChunkWriter,
};
#[derive(Debug)]
pub struct Putter<B: ?Sized> {
cursor: usize,
start: Bound<usize>,
end: Bound<usize>,
limit: Bound<usize>,
buf: ChunkWriter<B>,
}
impl<B: ChunkMut> From<B> for Putter<B> {
#[inline]
fn from(buf: B) -> Self {
Self::new(buf)
}
}
impl<B: EmptyChunk> EmptyChunk for Putter<B> {
#[inline]
fn empty() -> Self {
Self::new(B::empty())
}
}
impl<B> Putter<B> {
#[inline]
pub fn new(buf: impl Into<ChunkWriter<B>>) -> Self {
Self::with_cursor_and_bounds_inner(buf.into(), 0, Bound::Included(0), Bound::Unbounded)
}
#[inline]
pub const fn const_new(buf: B) -> Self {
let buf = ChunkWriter::new(buf);
Self::with_cursor_and_bounds_inner(buf, 0, Bound::Included(0), Bound::Unbounded)
}
#[inline]
pub fn with_limit(buf: impl Into<ChunkWriter<B>>, limit: usize) -> Self {
let write_buf = buf.into();
Self::with_cursor_and_bounds_inner(write_buf, 0, Bound::Included(0), Bound::Excluded(limit))
}
#[inline]
pub const fn const_with_limit(buf: ChunkWriter<B>, limit: usize) -> Self {
Self::with_cursor_and_bounds_inner(buf, 0, Bound::Included(0), Bound::Excluded(limit))
}
#[inline]
pub fn with_range(buf: impl Into<ChunkWriter<B>>, range: impl RangeBounds<usize>) -> Self
where
B: ChunkMut,
{
let start = range.start_bound().cloned();
let end = range.end_bound().cloned();
let write_buf = buf.into();
let start_pos = Self::resolve_start_bound(start, &write_buf);
Self::with_cursor_and_bounds_inner(write_buf, start_pos, Bound::Included(start_pos), end)
}
#[inline]
pub const fn position(&self) -> usize {
let start_pos = Self::resolve_start_bound_without_check(self.start);
self.cursor.saturating_sub(start_pos)
}
#[inline]
pub const fn absolute_position(&self) -> usize {
self.cursor
}
#[inline]
pub const fn reset_position(&mut self) {
self.cursor = Self::resolve_start_bound_without_check(self.start);
self.limit = self.end;
}
pub fn reset(&mut self)
where
B: ChunkMut,
{
self.reset_position();
self.fill(0);
}
#[inline]
pub fn into_inner(self) -> B {
self.buf.0
}
#[inline]
const fn with_cursor_and_bounds_inner(
buf: ChunkWriter<B>,
cursor: usize,
start: Bound<usize>,
end: Bound<usize>,
) -> Self {
Self {
buf,
cursor,
start,
end,
limit: end,
}
}
#[inline]
const fn resolve_start_bound_without_check(bound: Bound<usize>) -> usize {
match bound {
Bound::Included(n) => n,
Bound::Excluded(n) => n.saturating_add(1),
Bound::Unbounded => 0,
}
}
}
impl<B: ?Sized> Putter<B> {
#[inline]
fn resolve_start_bound(bound: Bound<usize>, buf: &ChunkWriter<B>) -> usize
where
B: ChunkMut,
{
let pos = match bound {
Bound::Included(n) => n,
Bound::Excluded(n) => n.saturating_add(1),
Bound::Unbounded => 0,
};
pos.min(buf.remaining_mut())
}
#[inline]
fn resolve_end_bound(&self, bound: Bound<usize>) -> usize
where
B: ChunkMut,
{
match bound {
Bound::Included(n) => (n.saturating_add(1)).min(self.buf.remaining_mut()),
Bound::Excluded(n) => n.min(self.buf.remaining_mut()),
Bound::Unbounded => self.buf.remaining_mut(),
}
}
}
impl<B: ChunkMut + ?Sized> ChunkMut for Putter<B> {
#[inline]
fn remaining_mut(&self) -> usize {
let end_pos = self.resolve_end_bound(self.limit);
end_pos.saturating_sub(self.cursor)
}
#[inline]
fn buffer_mut(&mut self) -> &mut [u8] {
let start = self.cursor.min(self.buf.remaining_mut());
let end_pos = self.resolve_end_bound(self.limit);
&mut self.buf.buffer_mut()[start..end_pos]
}
#[inline]
fn advance_mut(&mut self, cnt: usize) {
let remaining = self.remaining_mut();
if cnt > remaining {
panic_advance(&TryAdvanceError::new(must_non_zero(cnt), remaining));
}
self.cursor += cnt;
}
#[inline]
fn try_advance_mut(&mut self, cnt: usize) -> Result<(), TryAdvanceError> {
let remaining = self.remaining_mut();
if cnt > remaining {
return Err(TryAdvanceError::new(must_non_zero(cnt), remaining));
}
self.cursor += cnt;
Ok(())
}
#[inline]
fn truncate_mut(&mut self, new_len: usize) {
let current_remaining = self.remaining_mut();
if new_len >= current_remaining {
return; }
let new_end_pos = self.cursor + new_len;
let current_end_pos = self.resolve_end_bound(self.limit);
if new_end_pos < current_end_pos {
self.limit = Bound::Excluded(new_end_pos);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_putter_basic_functionality() {
let mut data = [0u8; 10];
let mut putter = Putter::new(&mut data[..]);
assert_eq!(putter.remaining_mut(), 10);
assert_eq!(putter.position(), 0);
putter.write_u8(0x42);
assert_eq!(putter.remaining_mut(), 9);
assert_eq!(putter.position(), 1);
assert_eq!(data[0], 0x42);
}
#[test]
fn test_putter_ergonomic_api() {
let mut data = [0u8; 10];
let mut putter = Putter::new(&mut data[..]);
putter.put_u8(0x11);
assert_eq!(data[0], 0x11);
}
#[test]
fn test_putter_with_limit() {
let mut data = [0u8; 10];
let mut putter = Putter::with_limit(&mut data[..], 3);
assert_eq!(putter.remaining_mut(), 3);
putter.write_u8(0x11);
putter.write_u8(0x22);
putter.write_u8(0x33);
assert_eq!(putter.remaining_mut(), 0);
assert_eq!(data[0], 0x11);
assert_eq!(data[1], 0x22);
assert_eq!(data[2], 0x33);
assert_eq!(data[3], 0x00); }
#[test]
fn test_putter_reset_position() {
let mut data = [0u8; 10];
let mut putter = Putter::new(&mut data[..]);
putter.advance_mut(3);
assert_eq!(putter.position(), 3);
assert_eq!(putter.remaining_mut(), 7);
putter.reset_position();
assert_eq!(putter.position(), 0);
assert_eq!(putter.remaining_mut(), 10);
}
#[test]
fn test_putter_truncate() {
let mut data = [0u8; 10];
let mut putter = Putter::new(&mut data[..]);
putter.truncate_mut(3);
assert_eq!(putter.remaining_mut(), 3);
putter.write_u8(0x11);
putter.write_u8(0x22);
putter.write_u8(0x33);
assert_eq!(putter.remaining_mut(), 0);
assert_eq!(data[0], 0x11);
assert_eq!(data[1], 0x22);
assert_eq!(data[2], 0x33);
assert_eq!(data[3], 0x00); }
#[test]
fn test_putter_try_advance() {
let mut data = [0u8; 5];
let mut putter = Putter::new(&mut data[..]);
assert!(putter.try_advance_mut(2).is_ok());
assert_eq!(putter.position(), 2);
assert!(putter.try_advance_mut(5).is_err());
assert_eq!(putter.position(), 2); }
#[test]
#[should_panic(expected = "advance")]
fn test_putter_advance_panic() {
let mut data = [0u8; 3];
let mut putter = Putter::new(&mut data[..]);
putter.advance_mut(5); }
#[test]
fn test_putter_with_different_integer_types() {
let mut data = [0u8; 20];
let mut putter = Putter::new(&mut data[..]);
putter.write_u16_le(0x1234);
putter.write_u32_le(0x56789ABC);
assert_eq!(putter.position(), 6);
assert_eq!(&data[0..2], &[0x34, 0x12]); assert_eq!(&data[2..6], &[0xBC, 0x9A, 0x78, 0x56]); }
#[test]
fn test_putter_write_slice() {
let mut data = [0u8; 10];
let mut putter = Putter::new(&mut data[..]);
let test_data = [0x11, 0x22, 0x33, 0x44];
putter.write_slice(&test_data);
assert_eq!(putter.position(), 4);
assert_eq!(&data[0..4], &test_data);
assert_eq!(data[4], 0x00); }
#[test]
fn test_putter_with_advanced_buffer() {
let mut data = [0u8; 10];
let mut buf = &mut data[..];
buf.advance_mut(3);
assert_eq!(buf.remaining_mut(), 7);
let mut putter = Putter::new(buf);
assert_eq!(putter.remaining_mut(), 7);
putter.put_u8(0x42);
assert_eq!(data[3], 0x42); }
#[test]
fn test_putter_buffer_mut_access() {
let mut data = [0u8; 10];
let mut putter = Putter::new(&mut data[..]);
putter.write_u8(0x11);
putter.write_u8(0x22);
let remaining_buffer = putter.buffer_mut();
remaining_buffer[0] = 0x99;
assert_eq!(data[0], 0x11);
assert_eq!(data[1], 0x22);
assert_eq!(data[2], 0x99);
}
#[test]
fn test_putter_state_consistency() {
let mut data = [0u8; 10];
let mut putter = Putter::new(&mut data[..]);
let initial_remaining = putter.remaining_mut();
let initial_written = putter.position();
putter.advance_mut(3);
assert_eq!(
putter.remaining_mut() + putter.position(),
initial_remaining
);
putter.truncate_mut(5);
assert_eq!(putter.remaining_mut(), 5);
assert_eq!(putter.position(), 3);
putter.reset_position();
assert_eq!(putter.position(), initial_written);
assert_eq!(putter.remaining_mut(), initial_remaining); }
#[test]
fn test_putter_exhaustive_write() {
let mut data = [0u8; 3];
let mut putter = Putter::new(&mut data[..]);
assert_eq!(putter.write_u8(0x11), 1);
assert_eq!(putter.remaining_mut(), 2);
assert_eq!(putter.write_u8(0x22), 1);
assert_eq!(putter.remaining_mut(), 1);
assert_eq!(putter.write_u8(0x33), 1);
assert_eq!(putter.remaining_mut(), 0);
assert_eq!(data, [0x11, 0x22, 0x33]);
}
#[test]
fn test_putter_from_trait() {
let mut data = [0u8; 10];
let putter: Putter<_> = (&mut data[..]).into();
assert_eq!(putter.remaining_mut(), 10);
}
#[test]
fn test_putter_endianness() {
let mut data = [0u8; 16];
let mut putter = Putter::new(&mut data[..]);
putter.write_u16_le(0x1234);
putter.write_u16_be(0x1234);
putter.write_u32_le(0x12345678);
putter.write_u32_be(0x12345678);
assert_eq!(&data[0..2], &[0x34, 0x12]);
assert_eq!(&data[2..4], &[0x12, 0x34]);
assert_eq!(&data[4..8], &[0x78, 0x56, 0x34, 0x12]);
assert_eq!(&data[8..12], &[0x12, 0x34, 0x56, 0x78]);
}
#[test]
fn test_putter_signed_values() {
let mut data = [0u8; 10];
let mut putter = Putter::new(&mut data[..]);
putter.write_i8(-1);
putter.write_i16_le(-1);
putter.write_i32_be(-1);
assert_eq!(data[0], 0xFF);
assert_eq!(&data[1..3], &[0xFF, 0xFF]);
assert_eq!(&data[3..7], &[0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_putter_reset_position_preserves_limits() {
let mut data = [0u8; 10];
let mut putter = Putter::with_limit(&mut data[..], 5);
putter.advance_mut(3);
assert_eq!(putter.position(), 3);
assert_eq!(putter.remaining_mut(), 2);
putter.reset_position();
assert_eq!(putter.position(), 0);
assert_eq!(putter.remaining_mut(), 5); }
#[test]
fn test_putter_truncate_after_advance() {
let mut data = [0u8; 10];
let mut putter = Putter::new(&mut data[..]);
putter.advance_mut(3);
assert_eq!(putter.remaining_mut(), 7);
putter.truncate_mut(4);
assert_eq!(putter.remaining_mut(), 4);
putter.write_slice(&[0x11, 0x22, 0x33, 0x44]);
assert_eq!(putter.remaining_mut(), 0);
assert_eq!(&data[3..7], &[0x11, 0x22, 0x33, 0x44]);
}
#[test]
fn test_putter_truncate_limited_putter() {
let mut data = [0u8; 10];
let mut putter = Putter::with_limit(&mut data[..], 6);
assert_eq!(putter.remaining_mut(), 6);
putter.truncate_mut(4);
assert_eq!(putter.remaining_mut(), 4);
putter.truncate_mut(2);
assert_eq!(putter.remaining_mut(), 2);
}
#[test]
fn test_error_details() {
let mut data = [0u8; 3];
let mut putter = Putter::new(&mut data[..]);
let advance_err = putter.try_advance_mut(5).unwrap_err();
assert_eq!(advance_err.requested().get(), 5);
assert_eq!(advance_err.available(), 3);
}
#[test]
fn test_putter_large_data() {
let mut large_data = [0u8; 1000];
let mut putter = Putter::new(&mut large_data[..]);
assert_eq!(putter.remaining_mut(), 1000);
for i in 0..100 {
putter.write_u8(i as u8);
}
assert_eq!(putter.remaining_mut(), 900);
assert_eq!(putter.position(), 100);
#[allow(clippy::needless_range_loop)]
for i in 0..100 {
assert_eq!(large_data[i], i as u8);
}
}
#[test]
fn test_putter_boundary_conditions() {
let mut single_byte = [0u8; 1];
let mut putter = Putter::new(&mut single_byte[..]);
assert_eq!(putter.remaining_mut(), 1);
putter.write_u8(0x42);
assert_eq!(putter.remaining_mut(), 0);
assert!(putter.try_advance_mut(1).is_err());
assert_eq!(putter.put_u8_checked(0x99), None);
assert!(putter.try_put_u8(0x99).is_err());
putter.reset_position();
assert_eq!(putter.remaining_mut(), 1);
}
#[test]
fn test_putter_with_limit_larger_than_buffer() {
let mut data = [0u8; 3];
let putter = Putter::with_limit(&mut data[..], 10);
assert_eq!(putter.remaining_mut(), 3);
}
#[test]
fn test_putter_with_limit_zero() {
let mut data = [0u8; 5];
let putter = Putter::with_limit(&mut data[..], 0);
assert_eq!(putter.remaining_mut(), 0);
}
#[test]
fn test_written_calculation() {
let mut data = [0u8; 10];
let mut putter = Putter::new(&mut data[..]);
assert_eq!(putter.position(), 0);
putter.advance_mut(2);
assert_eq!(putter.position(), 2);
putter.write_u8(0x42);
assert_eq!(putter.position(), 3);
putter.write_slice(&[1, 2, 3]);
assert_eq!(putter.position(), 6);
putter.reset_position();
assert_eq!(putter.position(), 0);
}
#[test]
fn test_putter_ergonomic_comparison() {
let mut data = [0u8; 10];
let mut putter = Putter::new(&mut data[..]);
putter.write_u8(0x42);
assert_eq!(putter.position(), 1);
assert_eq!(data[0], 0x42);
}
#[test]
fn test_putter_with_range() {
let mut data = [0u8; 10];
let mut putter = Putter::with_range(&mut data[..], 2..=8);
assert_eq!(putter.remaining_mut(), 7);
putter.write_u8(0x42);
assert_eq!(putter.position(), 1);
putter.reset();
assert_eq!(data[2], 0);
let mut putter = Putter::with_range(&mut data[..], ..7);
assert_eq!(putter.remaining_mut(), 7);
putter.write_u8(0x99);
assert_eq!(putter.position(), 1);
putter.reset();
assert_eq!(data[0], 0);
let mut putter = Putter::with_range(&mut data[..], (Bound::Excluded(1), Bound::Unbounded));
assert_eq!(putter.remaining_mut(), 8);
putter.write_u8(0x77);
assert_eq!(putter.position(), 1);
putter.reset();
assert_eq!(data[2], 0);
}
}