use crate::{binary::bits, Conceal};
use std::io::{self, BufReader, BufWriter, Read, Write};
#[derive(Debug)]
pub struct Carrier<P, W>
where
P: FnMut(usize) -> Option<u8>,
W: Write,
{
pattern: P,
writer: BufWriter<W>,
len: Option<u64>,
}
impl<P, W> Carrier<P, W>
where
P: FnMut(usize) -> Option<u8>,
W: Write,
{
pub fn with_embedded_len(len: usize, pattern: P, writer: W) -> Self {
Self {
pattern,
writer: BufWriter::new(writer),
len: Some(len as u64),
}
}
#[must_use]
pub fn new(pattern: P, writer: W) -> Self {
Self {
pattern,
writer: BufWriter::new(writer),
len: None,
}
}
}
impl<M, W> Conceal for &mut Carrier<M, W>
where
M: FnMut(usize) -> Option<u8>,
W: Write,
{
type Err = io::Error;
fn conceal<P: Read, C: Read>(self, payload: P, cover: C) -> io::Result<usize> {
let len_bytes = self
.len
.map(|len| len.to_be_bytes().to_vec())
.unwrap_or_default();
let mut cover = BufReader::new(cover);
let mut payload_bytes = len_bytes.chain(BufReader::new(payload)).bytes();
let mut payload_byte = match payload_bytes.next() {
Some(byte) => byte?,
_ => return Ok(io::copy(&mut cover, &mut self.writer)? as usize),
};
let mut payload_bytes_written = 0u64;
let mut bytes_written = 0;
let mut bit_count = 0u8;
for (index, cover_byte) in cover.by_ref().bytes().enumerate() {
let Some(mask) = (self.pattern)(index) else {
break;
};
let mut package_byte = cover_byte? & !mask;
for pow in bits::Ones::from(mask) {
package_byte |= (payload_byte & 1) << pow;
payload_byte >>= 1;
bit_count += 1;
if bit_count < 8 {
continue;
}
payload_bytes_written += 1;
if self
.len
.is_some_and(|n| payload_bytes_written >= 8 && payload_bytes_written - 8 >= n)
{
break;
}
payload_byte = match payload_bytes.next() {
Some(byte) => byte?,
None => break,
};
bit_count = 0;
}
bytes_written += self.writer.write(&[package_byte])?;
if bit_count == 8 {
break;
}
}
if match self.len {
Some(n) => payload_bytes_written < 8 || payload_bytes_written - 8 < n,
None => payload_bytes.next().is_some(),
} {
return Err(io::Error::from(io::ErrorKind::WriteZero));
}
bytes_written += io::copy(&mut cover, &mut self.writer)? as usize;
self.writer.flush()?;
Ok(bytes_written)
}
}