use std::convert::TryInto;
use std::io::{Error, ErrorKind, Result};
use super::Stream;
use crate::utils::RawFdContainer;
const MINIMAL_PACKET_LENGTH: usize = 32;
#[derive(Debug)]
pub(crate) struct PacketReader {
read_buffer: Box<[u8]>,
pending_packet: Vec<u8>,
already_read: usize,
}
impl PacketReader {
pub(crate) fn new() -> Self {
Self {
read_buffer: vec![0; 4096].into_boxed_slice(),
pending_packet: vec![0; MINIMAL_PACKET_LENGTH],
already_read: 0,
}
}
fn handle_partial_read(&mut self, nread: usize, out_packets: &mut Vec<Vec<u8>>) {
self.already_read += nread;
if self.already_read == MINIMAL_PACKET_LENGTH {
let extra = extra_length(self.pending_packet[..].try_into().unwrap());
self.pending_packet.reserve_exact(extra);
self.pending_packet.resize(MINIMAL_PACKET_LENGTH + extra, 0);
}
if self.already_read == self.pending_packet.len() {
let initial_packet = &self.pending_packet[0..MINIMAL_PACKET_LENGTH]
.try_into()
.unwrap();
let extra = extra_length(initial_packet);
assert_eq!(self.pending_packet.len(), MINIMAL_PACKET_LENGTH + extra);
out_packets.push(std::mem::replace(
&mut self.pending_packet,
vec![0; MINIMAL_PACKET_LENGTH],
));
self.already_read = 0;
}
}
pub(crate) fn try_read_packets(
&mut self,
stream: &impl Stream,
out_packets: &mut Vec<Vec<u8>>,
fd_storage: &mut Vec<RawFdContainer>,
) -> Result<()> {
loop {
if (self.pending_packet.len() - self.already_read) >= self.read_buffer.len() {
assert_ne!(self.already_read, self.pending_packet.len());
match stream.read(&mut self.pending_packet[self.already_read..], fd_storage) {
Ok(0) => {
return Err(Error::new(
ErrorKind::UnexpectedEof,
"The X11 server closed the connection",
));
}
Ok(nread) => self.handle_partial_read(nread, out_packets),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => break,
Err(e) => return Err(e),
}
} else {
match stream.read(&mut self.read_buffer, fd_storage) {
Ok(0) => {
return Err(Error::new(
ErrorKind::UnexpectedEof,
"The X11 server closed the connection",
));
}
Ok(nread) => {
let mut used_from_buffer = 0;
while used_from_buffer != nread {
let rem_read_buffer = &self.read_buffer[used_from_buffer..nread];
let rem_packet = &mut self.pending_packet[self.already_read..];
let to_copy = rem_read_buffer.len().min(rem_packet.len());
assert_ne!(to_copy, 0);
rem_packet[..to_copy].copy_from_slice(&rem_read_buffer[..to_copy]);
used_from_buffer += to_copy;
self.handle_partial_read(to_copy, out_packets);
}
}
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => break,
Err(e) => return Err(e),
}
}
}
Ok(())
}
}
fn extra_length(buffer: &[u8; MINIMAL_PACKET_LENGTH]) -> usize {
use crate::protocol::xproto::GE_GENERIC_EVENT;
let response_type = buffer[0];
const REPLY: u8 = 1;
if response_type == REPLY || response_type & 0x7f == GE_GENERIC_EVENT {
let length_field = buffer[4..8].try_into().unwrap();
let length_field = u32::from_ne_bytes(length_field) as usize;
4 * length_field
} else {
0
}
}