use crate::{
connection::{advance_io, new_io_slice, IoSlice},
Error, Fd, Result,
};
use alloc::{boxed::Box, vec::Vec};
use core::{convert::TryFrom, mem};
use x11rb_protocol::{
connection::ReplyFdKind,
x11_utils::{ReplyFDsRequest, ReplyRequest, Request, TryParseFd, VoidRequest},
DiscardMode,
};
pub struct RawRequest<'target, 'req> {
data: &'target mut [IoSlice<'req>],
fds: Vec<Fd>,
advanced: usize,
variant: ReplyFdKind,
extension_name: Option<&'static str>,
discard_reply: bool,
buffer: Option<&'req mut [u8; 8]>,
}
pub(crate) struct BufferedRequest {
data: Vec<u8>,
fds: Vec<Fd>,
advanced: usize,
variant: ReplyFdKind,
discard_reply: bool,
extension_name: Option<&'static str>,
buffer: [u8; 8],
}
fn from_request<R: Request, Ret>(
request: R,
variant: ReplyFdKind,
discard_reply: bool,
f: impl FnOnce(RawRequest<'_, '_>) -> Ret,
) -> Ret {
let (serialized, fds) = request.serialize(u8::MAX);
let mut slices = [
new_io_slice(&[]),
new_io_slice(&[]),
new_io_slice(&serialized),
];
let mut buffer = [0; 8];
let mut req = RawRequest::new(&mut slices, fds, variant, R::EXTENSION_NAME, &mut buffer);
if discard_reply {
req.discard_reply();
}
f(req)
}
pub fn from_void_request<R: VoidRequest, Ret>(
request: R,
discard_reply: bool,
f: impl FnOnce(RawRequest<'_, '_>) -> Ret,
) -> Ret {
from_request(request, ReplyFdKind::NoReply, discard_reply, f)
}
pub fn from_reply_request<R: ReplyRequest, Ret>(
request: R,
f: impl FnOnce(RawRequest<'_, '_>) -> Ret,
) -> Ret {
from_request(request, ReplyFdKind::ReplyWithoutFDs, false, f)
}
pub fn from_reply_fds_request<R: ReplyFDsRequest, Ret>(
request: R,
f: impl FnOnce(RawRequest<'_, '_>) -> Ret,
) -> Ret {
from_request(request, ReplyFdKind::ReplyWithFDs, false, f)
}
impl<'target, 'req> RawRequest<'target, 'req> {
pub fn new(
data: &'target mut [IoSlice<'req>],
fds: Vec<Fd>,
variant: ReplyFdKind,
extension_name: Option<&'static str>,
buffer: &'req mut [u8; 8],
) -> Self {
assert!(data.len() >= 3);
assert!(data[2].len() >= 4);
Self {
data,
fds,
advanced: 0,
variant,
extension_name,
discard_reply: false,
buffer: Some(buffer),
}
}
fn discard_reply(&mut self) {
self.discard_reply = true;
}
#[must_use]
pub fn discard_mode(&self) -> Option<DiscardMode> {
if self.discard_reply {
Some(DiscardMode::DiscardReply)
} else {
None
}
}
#[must_use]
pub fn variant(&self) -> ReplyFdKind {
self.variant
}
#[must_use]
pub fn into_raw_parts(self) -> (Box<[u8]>, Vec<Fd>) {
let BufferedRequest { data, fds, .. } = self.into();
(data.into_boxed_slice(), fds)
}
#[allow(clippy::cast_possible_truncation)]
pub fn format(&mut self, ext_opcode: Option<u8>, max_len: usize) -> Result<()> {
let len = self.len();
let x_len = len / 4;
if x_len > max_len {
return Err(Error::make_large_request(x_len, max_len));
}
if let Ok(x_len) = u16::try_from(x_len) {
let [l1, l2] = x_len.to_ne_bytes();
let buffer = self.buffer.as_mut().unwrap();
buffer[0] = match ext_opcode {
Some(x) => x,
None => self.data[2][0],
};
buffer[1] = self.data[2][1];
buffer[2] = l1;
buffer[3] = l2;
let buffer = self.buffer.take().unwrap();
self.data[1] = new_io_slice(&buffer[..4]);
advance_io(&mut self.data[2], 4);
} else {
let length_bytes = ((x_len + 1) as u32).to_ne_bytes();
let buffer = self.buffer.as_mut().unwrap();
buffer[0] = match ext_opcode {
Some(x) => x,
None => self.data[2][0],
};
buffer[1] = self.data[2][1];
buffer[2] = 0;
buffer[3] = 0;
buffer[4..8].copy_from_slice(&length_bytes);
self.data[1] = new_io_slice(self.buffer.take().unwrap());
advance_io(&mut self.data[2], 4);
}
Ok(())
}
#[must_use]
pub fn extension(&self) -> Option<&'static str> {
self.extension_name
}
pub fn len(&self) -> usize {
self.data
.iter()
.map(|buf| buf.len())
.fold(0, usize::saturating_add)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.iter().all(|buf| buf.is_empty())
}
#[allow(clippy::mut_mut)]
pub fn mut_parts(&mut self) -> (&mut &'target mut [IoSlice<'req>], &mut Vec<Fd>) {
(&mut self.data, &mut self.fds)
}
pub fn advance(&mut self, bytes: usize) {
self.advanced += bytes;
let mut to_remove = 0;
let mut total_len = 0;
for buf in self.data.iter() {
if total_len + buf.len() > bytes {
break;
}
total_len += buf.len();
to_remove += 1;
}
tracing::trace!(
"Advancing by {} bytes, removing {} slices and {} bytes",
bytes,
to_remove,
bytes - total_len,
);
self.data = &mut mem::take(&mut self.data)[to_remove..];
if !self.data.is_empty() {
advance_io(&mut self.data[0], bytes - total_len);
}
}
}
impl BufferedRequest {
pub(crate) fn take<Ret>(&mut self, f: impl FnOnce(RawRequest<'_, '_>) -> Ret) -> Ret {
let raw = RawRequest {
buffer: Some(&mut self.buffer),
data: &mut [
new_io_slice(&[]),
new_io_slice(&[]),
new_io_slice(&self.data[self.advanced..]),
],
fds: mem::take(&mut self.fds),
advanced: self.advanced,
variant: self.variant,
discard_reply: self.discard_reply,
extension_name: self.extension_name,
};
f(raw)
}
}
cfg_async! {
impl BufferedRequest {
pub(crate) fn borrow<Ret>(&mut self, f: impl FnOnce(&mut RawRequest<'_, '_>) -> Ret) -> Ret {
tracing::trace!(data_len = self.data.len());
let mut raw = RawRequest {
buffer: Some(&mut self.buffer),
data: &mut [
new_io_slice(&[]),
new_io_slice(&[]),
new_io_slice(&self.data[self.advanced..]),
],
fds: mem::take(&mut self.fds),
advanced: self.advanced,
variant: self.variant,
discard_reply: self.discard_reply,
extension_name: self.extension_name,
};
let ret = f(&mut raw);
let merge_buffer = if raw.data.len() > 2 && raw.data[1].len() > 0 {
Some(raw.data[1].len())
} else {
None
};
self.advanced = raw.advanced;
self.fds = mem::take(&mut raw.fds);
if self.advanced >= self.data.len() {
self.data.clear();
return ret;
}
if let Some(merge_len) = merge_buffer {
tracing::trace!(merge_len, "merging buffer into data");
self.data
.splice(0..4, self.buffer[..merge_len].iter().copied());
}
tracing::trace!("{:?}", &self.data);
ret
}
}
}
impl<'target, 'req> From<RawRequest<'target, 'req>> for BufferedRequest {
fn from(raw: RawRequest<'target, 'req>) -> Self {
let mut data = Vec::with_capacity(raw.len() + 4);
for buf in raw.data.iter() {
data.extend_from_slice(buf);
}
Self {
data,
advanced: raw.advanced,
variant: raw.variant,
extension_name: raw.extension_name,
fds: raw.fds,
discard_reply: raw.discard_reply,
buffer: match raw.buffer {
Some(buf) => *buf,
None => [0; 8],
},
}
}
}
pub struct RawReply {
data: Box<[u8]>,
fds: Vec<Fd>,
}
impl RawReply {
#[must_use]
pub fn new(data: Box<[u8]>, fds: Vec<Fd>) -> Self {
Self { data, fds }
}
pub fn into_reply<T: TryParseFd>(mut self) -> Result<T> {
let (val, _) =
T::try_parse_fd(&self.data, &mut self.fds).map_err(Error::make_parse_error)?;
Ok(val)
}
}