use std::io::{self, BufReader, BufWriter, Read, Write};
use super::constants::{PAD_CHAR, RECORD_LEN};
pub struct RecordWriter<W: Write> {
inner: BufWriter<W>,
buffer: Vec<u8>,
}
impl<W: Write> RecordWriter<W> {
pub fn new(writer: W) -> Self {
Self {
inner: BufWriter::new(writer),
buffer: Vec::with_capacity(RECORD_LEN),
}
}
pub fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> {
for &byte in data {
self.buffer.push(byte);
if self.buffer.len() == RECORD_LEN {
self.flush_buffer()?;
}
}
Ok(())
}
pub fn write_record(&mut self, record: &[u8; RECORD_LEN]) -> io::Result<()> {
if !self.buffer.is_empty() {
self.pad_and_flush()?;
}
self.inner.write_all(record)
}
pub fn write_string(&mut self, s: &str, len: usize) -> io::Result<()> {
let bytes = s.as_bytes();
let write_len = bytes.len().min(len);
self.write_bytes(&bytes[..write_len])?;
for _ in write_len..len {
self.write_bytes(&[PAD_CHAR])?;
}
Ok(())
}
pub fn pad_and_flush(&mut self) -> io::Result<()> {
if self.buffer.is_empty() {
return Ok(());
}
while self.buffer.len() < RECORD_LEN {
self.buffer.push(PAD_CHAR);
}
self.flush_buffer()
}
pub fn finish(mut self) -> io::Result<W> {
self.pad_and_flush()?;
self.inner.flush()?;
Ok(self.inner.into_inner()?)
}
#[must_use]
pub fn buffer_position(&self) -> usize {
self.buffer.len()
}
fn flush_buffer(&mut self) -> io::Result<()> {
self.inner.write_all(&self.buffer)?;
self.buffer.clear();
Ok(())
}
}
pub struct RecordReader<R: Read> {
inner: BufReader<R>,
buffer: [u8; RECORD_LEN],
position: usize,
at_eof: bool,
}
impl<R: Read> RecordReader<R> {
pub fn new(reader: R) -> Self {
Self {
inner: BufReader::new(reader),
buffer: [0u8; RECORD_LEN],
position: RECORD_LEN, at_eof: false,
}
}
pub fn read_bytes(&mut self, len: usize) -> io::Result<Vec<u8>> {
let mut result = Vec::with_capacity(len);
for _ in 0..len {
if self.position >= RECORD_LEN {
self.read_next_record()?;
}
result.push(self.buffer[self.position]);
self.position += 1;
}
Ok(result)
}
pub fn read_record(&mut self) -> io::Result<[u8; RECORD_LEN]> {
if self.position != 0 && self.position != RECORD_LEN {
self.position = RECORD_LEN;
}
self.read_next_record()?;
self.position = RECORD_LEN;
Ok(self.buffer)
}
pub fn read_string(&mut self, len: usize) -> io::Result<String> {
let bytes = self.read_bytes(len)?;
let s = String::from_utf8_lossy(&bytes);
Ok(s.trim_end().to_string())
}
pub fn skip_to_record_boundary(&mut self) -> io::Result<()> {
if self.position != 0 && self.position < RECORD_LEN {
self.position = RECORD_LEN;
}
Ok(())
}
pub fn skip_bytes(&mut self, n: usize) -> io::Result<()> {
for _ in 0..n {
if self.position >= RECORD_LEN {
self.read_next_record()?;
}
self.position += 1;
}
Ok(())
}
#[must_use]
pub fn at_eof(&self) -> bool {
self.at_eof
}
fn read_next_record(&mut self) -> io::Result<()> {
match self.inner.read_exact(&mut self.buffer) {
Ok(()) => {
self.position = 0;
Ok(())
}
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
self.at_eof = true;
Err(e)
}
Err(e) => Err(e),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_record_writer() {
let mut output = Vec::new();
{
let mut writer = RecordWriter::new(&mut output);
writer.write_string("TEST", 8).unwrap();
writer.pad_and_flush().unwrap();
}
assert_eq!(output.len(), RECORD_LEN);
assert_eq!(&output[..4], b"TEST");
assert!(output[4..].iter().all(|&b| b == PAD_CHAR));
}
#[test]
fn test_record_reader() {
let mut data = [PAD_CHAR; RECORD_LEN];
data[..4].copy_from_slice(b"TEST");
let mut reader = RecordReader::new(Cursor::new(data));
let s = reader.read_string(8).unwrap();
assert_eq!(s, "TEST");
}
}