use std::io::Write;
use crate::checksum::Md5State;
use crate::error::{Par2Error, Result};
use crate::types::RecoverySetId;
use super::header::{HEADER_SIZE, MAGIC};
pub(crate) fn encode_packet(
packet_type: &[u8; 16],
recovery_set_id: RecoverySetId,
body: &[u8],
) -> Result<Vec<u8>> {
let length =
HEADER_SIZE
.checked_add(body.len())
.ok_or_else(|| Par2Error::ResourceLimitExceeded {
reason: "packet length overflows addressable memory".to_string(),
})?;
if !length.is_multiple_of(4) {
return Err(Par2Error::InvalidPacketLength {
length: length as u64,
});
}
let packet_hash = packet_hash(recovery_set_id, packet_type, body);
let mut packet = Vec::with_capacity(length);
packet.extend_from_slice(&MAGIC[..]);
packet.extend_from_slice(&(length as u64).to_le_bytes());
packet.extend_from_slice(&packet_hash);
packet.extend_from_slice(recovery_set_id.as_bytes());
packet.extend_from_slice(packet_type);
packet.extend_from_slice(body);
Ok(packet)
}
pub(crate) fn packet_hash(
recovery_set_id: RecoverySetId,
packet_type: &[u8; 16],
body: &[u8],
) -> [u8; 16] {
let mut hasher = Md5State::new();
hasher.update(recovery_set_id.as_bytes());
hasher.update(packet_type);
hasher.update(body);
hasher.finalize()
}
pub(crate) fn start_streamed_hash(
recovery_set_id: RecoverySetId,
packet_type: &[u8; 16],
) -> Md5State {
let mut hasher = Md5State::new();
hasher.update(recovery_set_id.as_bytes());
hasher.update(packet_type);
hasher
}
pub(crate) fn encode_header(
packet_type: &[u8; 16],
recovery_set_id: RecoverySetId,
length: u64,
packet_hash: [u8; 16],
) -> Result<[u8; HEADER_SIZE]> {
if length < HEADER_SIZE as u64 || !length.is_multiple_of(4) {
return Err(Par2Error::InvalidPacketLength { length });
}
let mut header = [0u8; HEADER_SIZE];
header[0..8].copy_from_slice(&MAGIC[..]);
header[8..16].copy_from_slice(&length.to_le_bytes());
header[16..32].copy_from_slice(&packet_hash);
header[32..48].copy_from_slice(recovery_set_id.as_bytes());
header[48..64].copy_from_slice(packet_type);
Ok(header)
}
#[allow(dead_code)]
pub(crate) fn write_packet<W: Write>(
writer: &mut W,
packet_type: &[u8; 16],
recovery_set_id: RecoverySetId,
body: &[u8],
) -> Result<()> {
writer
.write_all(&encode_packet(packet_type, recovery_set_id, body)?)
.map_err(Par2Error::Io)
}