use std::collections::VecDeque;
use std::io::{self, Read, Write};
use std::sync::{Arc, Condvar, Mutex};
use crate::net::proto::Msg;
use crate::{Error, Result};
pub const MAX_FRAME: u64 = 256 * 1024 * 1024;
pub fn send_msg<W: Write>(w: &mut W, msg: &Msg) -> Result<()> {
let bytes = bincode::serialize(msg).map_err(|e| Error::Protocol(format!("serialize: {e}")))?;
let len = bytes.len() as u64;
if len > MAX_FRAME {
return Err(Error::Protocol(format!("frame too large: {len} bytes")));
}
w.write_all(&len.to_le_bytes())?;
w.write_all(&bytes)?;
w.flush()?;
Ok(())
}
pub fn recv_msg<R: Read>(r: &mut R) -> Result<Msg> {
let mut len_buf = [0u8; 8];
r.read_exact(&mut len_buf)?;
let len = u64::from_le_bytes(len_buf);
if len > MAX_FRAME {
return Err(Error::Protocol(format!("frame too large: {len} bytes")));
}
let len_usize = usize::try_from(len)
.map_err(|_| Error::Protocol("frame length overflows usize".to_string()))?;
let mut buf = vec![0u8; len_usize];
r.read_exact(&mut buf)?;
bincode::deserialize(&buf).map_err(|e| Error::Protocol(format!("deserialize: {e}")))
}
pub struct IoDuplex<R: Read, W: Write> {
pub r: R,
pub w: W,
}
impl<R: Read, W: Write> IoDuplex<R, W> {
pub fn new(r: R, w: W) -> Self {
Self { r, w }
}
}
impl<R: Read, W: Write> Read for IoDuplex<R, W> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.r.read(buf)
}
}
impl<R: Read, W: Write> Write for IoDuplex<R, W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.w.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.w.flush()
}
}
struct PipeState {
buf: VecDeque<u8>,
closed: bool,
}
pub struct PipeWriter {
inner: Arc<(Mutex<PipeState>, Condvar)>,
}
pub struct PipeReader {
inner: Arc<(Mutex<PipeState>, Condvar)>,
}
fn new_pipe() -> (PipeWriter, PipeReader) {
let inner = Arc::new((
Mutex::new(PipeState {
buf: VecDeque::new(),
closed: false,
}),
Condvar::new(),
));
(
PipeWriter {
inner: Arc::clone(&inner),
},
PipeReader { inner },
)
}
impl Write for PipeWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let (lock, cvar) = &*self.inner;
let mut state = lock.lock().unwrap();
state.buf.extend(buf.iter().copied());
cvar.notify_all();
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Drop for PipeWriter {
fn drop(&mut self) {
let (lock, cvar) = &*self.inner;
let mut state = lock.lock().unwrap();
state.closed = true;
cvar.notify_all();
}
}
impl Read for PipeReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if buf.is_empty() {
return Ok(0);
}
let (lock, cvar) = &*self.inner;
let mut state = lock.lock().unwrap();
loop {
if !state.buf.is_empty() {
let n = state.buf.len().min(buf.len());
for slot in buf.iter_mut().take(n) {
*slot = state.buf.pop_front().unwrap();
}
return Ok(n);
}
if state.closed {
return Ok(0); }
state = cvar.wait(state).unwrap();
}
}
}
#[must_use]
pub fn duplex_pair() -> (
IoDuplex<PipeReader, PipeWriter>,
IoDuplex<PipeReader, PipeWriter>,
) {
let (a_to_b_w, a_to_b_r) = new_pipe();
let (b_to_a_w, b_to_a_r) = new_pipe();
let end_a = IoDuplex::new(b_to_a_r, a_to_b_w);
let end_b = IoDuplex::new(a_to_b_r, b_to_a_w);
(end_a, end_b)
}