Files
another-boids-in-rust/vendor/x11rb-protocol/src/packet_reader.rs

281 lines
8.6 KiB
Rust

//! Collects X11 data into "packets" to be parsed by a display.
use core::fmt;
use core::mem::replace;
use alloc::{vec, vec::Vec};
/// Minimal length of an X11 packet.
const MINIMAL_PACKET_LENGTH: usize = 32;
/// A wrapper around a buffer used to read X11 packets.
pub struct PacketReader {
/// A partially-read packet.
pending_packet: Vec<u8>,
/// The point at which the packet is already read.
already_read: usize,
}
impl fmt::Debug for PacketReader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PacketReader")
.field(&format_args!(
"{}/{}",
self.already_read,
self.pending_packet.len()
))
.finish()
}
}
impl Default for PacketReader {
fn default() -> Self {
Self::new()
}
}
impl PacketReader {
/// Create a new, empty `PacketReader`.
///
/// # Example
///
/// ```rust
/// # use x11rb_protocol::packet_reader::PacketReader;
/// let reader = PacketReader::new();
/// ```
pub fn new() -> Self {
Self {
pending_packet: vec![0; MINIMAL_PACKET_LENGTH],
already_read: 0,
}
}
/// Get the buffer that the reader should fill with data.
///
/// # Example
///
/// ```rust
/// # use x11rb_protocol::packet_reader::PacketReader;
/// # use x11rb_protocol::protocol::xproto::{GetInputFocusReply, InputFocus, Window};
/// let mut reader = PacketReader::new();
/// let buffer: [u8; 32] = read_in_buffer();
///
/// reader.buffer().copy_from_slice(&buffer);
///
/// # fn read_in_buffer() -> [u8; 32] { [0; 32] }
/// ```
pub fn buffer(&mut self) -> &mut [u8] {
&mut self.pending_packet[self.already_read..]
}
/// The remaining capacity that needs to be filled.
pub fn remaining_capacity(&self) -> usize {
self.pending_packet.len() - self.already_read
}
/// Advance this buffer by the given amount.
///
/// This will return the packet that was read, if enough bytes were read in order
/// to form a complete packet.
pub fn advance(&mut self, amount: usize) -> Option<Vec<u8>> {
self.already_read += amount;
debug_assert!(self.already_read <= self.pending_packet.len());
if self.already_read == MINIMAL_PACKET_LENGTH {
// we've read in the minimal packet, compute the amount of data we need to read
// to form a complete packet
let extra_length = extra_length(&self.pending_packet);
// tell if we need to read more
if extra_length > 0 {
let total_length = MINIMAL_PACKET_LENGTH + extra_length;
self.pending_packet.resize(total_length, 0);
return None;
}
} else if self.already_read != self.pending_packet.len() {
// we haven't read the full packet yet, return
return None;
}
// we've read in the full packet, return it
self.already_read = 0;
Some(replace(
&mut self.pending_packet,
vec![0; MINIMAL_PACKET_LENGTH],
))
}
}
/// Compute the length of the data we need to read, beyond the `MINIMAL_PACKET_LENGTH`.
fn extra_length(buffer: &[u8]) -> usize {
use crate::protocol::xproto::GE_GENERIC_EVENT;
const REPLY: u8 = 1;
let response_type = buffer[0];
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 {
// Fixed size packet: error or event that is not GE_GENERIC_EVENT
0
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::PacketReader;
use alloc::{vec, vec::Vec};
fn test_packets(packets: Vec<Vec<u8>>) {
// Combine all packet data into one big chunk and test that the packet reader splits things
let mut all_data = packets.iter().flatten().copied().collect::<Vec<u8>>();
let mut reader = PacketReader::default();
for (i, packet) in packets.into_iter().enumerate() {
std::println!("Checking packet {i}");
loop {
let buffer = reader.buffer();
let amount = std::cmp::min(buffer.len(), all_data.len());
buffer.copy_from_slice(&all_data[..amount]);
let _ = all_data.drain(..amount);
if let Some(read_packet) = reader.advance(amount) {
assert_eq!(read_packet, packet);
break;
}
}
}
}
fn make_reply_with_length(len: usize) -> Vec<u8> {
let mut packet = vec![0; len];
let len = (len - 32) / 4;
// write "len" to bytes 4..8 in the packet
let len_bytes = (len as u32).to_ne_bytes();
packet[4..8].copy_from_slice(&len_bytes);
packet[0] = 1;
packet
}
#[test]
fn fixed_size_packet() {
// packet with a fixed size
let packet = vec![0; 32];
test_packets(vec![packet]);
}
#[test]
fn variable_size_packet() {
// packet with a variable size
let packet = make_reply_with_length(1200);
test_packets(vec![packet]);
}
#[test]
fn test_many_fixed_size_packets() {
let mut packets = vec![];
for _ in 0..100 {
packets.push(vec![0; 32]);
}
test_packets(packets);
}
#[test]
fn test_many_variable_size_packets() {
let mut packets = vec![];
for i in 0..100 {
// for maximum variation, increase packet size in a curved parabola
// defined by -1/25 (x - 50)^2 + 100
let variation = ((i - 50) * (i - 50)) as f32;
let variation = -1.0 / 25.0 * variation + 100.0;
let variation = variation as usize;
// round to a multiple of 4
let variation = variation / 4 * 4;
let mut len = 1200 + variation;
let mut packet = vec![0; len];
assert_eq!(0, len % 4);
len = (len - 32) / 4;
// write "len" to bytes 4..8 in the packet
let len_bytes = (len as u32).to_ne_bytes();
packet[4..8].copy_from_slice(&len_bytes);
packet[0] = 1;
packets.push(packet);
}
test_packets(packets);
}
#[test]
fn test_many_size_packets_mixed() {
let mut packets = vec![];
for i in 0..100 {
// on odds, do a varsize packet
let mut len = if i & 1 == 1 {
// for maximum variation, increase packet size in a curved parabola
// defined by -1/25 (x - 50)^2 + 100
let variation = ((i - 50) * (i - 50)) as f32;
let variation = -1.0 / 25.0 * variation + 100.0;
let variation = variation as usize;
// round to a multiple of 4
let variation = variation / 4 * 4;
1200 + variation
} else {
32
};
assert_eq!(0, len % 4);
let mut packet = vec![0; len];
len = (len - 32) / 4;
// write "len" to bytes 4..8 in the packet
let len_bytes = (len as u32).to_ne_bytes();
packet[4..8].copy_from_slice(&len_bytes);
packet[0] = 1;
packets.push(packet);
}
test_packets(packets);
}
#[test]
fn test_debug_fixed_size_packet() {
// The debug output includes the length of the packet of the packet and how much was
// already read
let mut reader = PacketReader::new();
assert_eq!(std::format!("{reader:?}"), "PacketReader(0/32)");
let _ = reader.advance(15);
assert_eq!(std::format!("{reader:?}"), "PacketReader(15/32)");
let _ = reader.advance(15);
assert_eq!(std::format!("{reader:?}"), "PacketReader(30/32)");
let _ = reader.advance(2);
assert_eq!(std::format!("{reader:?}"), "PacketReader(0/32)");
}
#[test]
fn test_debug_variable_size_packet() {
let packet = make_reply_with_length(1200);
let mut reader = PacketReader::new();
let first_len = 32;
let second_len = 3;
reader.buffer()[..first_len].copy_from_slice(&packet[..first_len]);
let _ = reader.advance(first_len);
reader.buffer()[..second_len].copy_from_slice(&packet[..second_len]);
let _ = reader.advance(second_len);
assert_eq!(std::format!("{reader:?}"), "PacketReader(35/1200)");
}
}