use crate::error::ProtocolError;
use crate::manifest::ShareId;
use iroh::endpoint::{RecvStream, SendStream};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
pub(crate) const ALPN: &[u8] = b"chorrent/2";
const MAX_FRAME: usize = 16 * 1024 * 1024;
#[derive(Debug, Serialize, Deserialize)]
pub(crate) enum Request {
Manifest {
share: ShareId,
auth: Option<[u8; 32]>,
},
Subscribe {
share: ShareId,
auth: Option<[u8; 32]>,
},
Piece {
share: ShareId,
auth: Option<[u8; 32]>,
index: u32,
},
}
#[derive(Debug, Serialize, Deserialize)]
pub(crate) enum Response {
Manifest(Vec<u8>),
Bitfield(Vec<u8>),
Have(u32),
Piece(Vec<u8>),
DontHave,
NotFound,
Busy,
}
pub(crate) async fn write_frame<T: Serialize>(
send: &mut SendStream,
msg: &T,
) -> Result<(), ProtocolError> {
let body = postcard::to_allocvec(msg).map_err(|e| ProtocolError::Send {
message: e.to_string(),
})?;
let mut frame = Vec::with_capacity(4 + body.len());
frame.extend_from_slice(&(body.len() as u32).to_le_bytes());
frame.extend_from_slice(&body);
send.write_all(&frame)
.await
.map_err(|e| ProtocolError::Send {
message: e.to_string(),
})
}
pub(crate) async fn read_frame<T: DeserializeOwned>(
recv: &mut RecvStream,
) -> Result<Option<T>, ProtocolError> {
let mut len = [0u8; 4];
match recv.read_exact(&mut len).await {
Ok(()) => {}
Err(iroh::endpoint::ReadExactError::FinishedEarly(0)) => return Ok(None),
Err(e) => {
return Err(ProtocolError::Receive {
message: e.to_string(),
});
}
}
let len = u32::from_le_bytes(len) as usize;
if len > MAX_FRAME {
return Err(ProtocolError::Receive {
message: format!("frame of {len} bytes is too large"),
});
}
let mut body = vec![0u8; len];
recv.read_exact(&mut body)
.await
.map_err(|e| ProtocolError::Receive {
message: e.to_string(),
})?;
postcard::from_bytes(&body)
.map(Some)
.map_err(|e| ProtocolError::Receive {
message: e.to_string(),
})
}
pub(crate) async fn expect_frame<T: DeserializeOwned>(
recv: &mut RecvStream,
) -> Result<T, ProtocolError> {
read_frame(recv)
.await?
.ok_or_else(|| ProtocolError::Receive {
message: "peer closed the stream".into(),
})
}
pub(crate) fn pack_bits(have: &[bool]) -> Vec<u8> {
let mut out = vec![0u8; have.len().div_ceil(8)];
for (i, _) in have.iter().enumerate().filter(|(_, h)| **h) {
out[i / 8] |= 1 << (i % 8);
}
out
}
pub(crate) fn unpack_bits(bytes: &[u8], len: usize) -> Vec<bool> {
(0..len)
.map(|i| bytes.get(i / 8).is_some_and(|b| b & (1 << (i % 8)) != 0))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bits_round_trip_and_tolerate_short_input() {
let have = vec![
true, false, true, true, false, false, false, false, true, false,
];
assert_eq!(unpack_bits(&pack_bits(&have), have.len()), have);
assert_eq!(unpack_bits(&[0b1], 3), vec![true, false, false]);
assert_eq!(unpack_bits(&[], 2), vec![false, false]);
}
}