use crate::types::{
NewId, ObjectId, WlFixed, WlSlice, WlStr, i32_slice_to_u8_mut, u32_slice_to_u8,
u32_slice_to_u8_mut,
};
use ::alloc::{
alloc::{self, Layout},
boxed::Box,
};
use core::{mem::MaybeUninit, num::NonZeroU32};
use rustix::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
type WaylandBuffer = [u32; 0x40_00];
#[derive(Debug)]
pub struct Payload {
bytes: WaylandBuffer,
fds: [RawFd; 32],
cur: u16,
cur_fd: u8,
}
#[derive(Debug)]
pub struct Receiver {
payload: Box<Payload>,
}
#[derive(Debug)]
pub struct Messages<'a> {
sender_id: ObjectId,
op: u16,
bytes_received: u16,
fds_received: u8,
payload: &'a mut Payload,
}
impl Payload {
fn new() -> Box<Self> {
unsafe {
let layout = Layout::new::<Self>();
let ptr = alloc::alloc_zeroed(layout);
if ptr.is_null() {
alloc::handle_alloc_error(layout);
}
Box::from_raw(ptr.cast())
}
}
#[inline]
#[must_use]
pub fn bytes(&self) -> &[u32] {
self.bytes.as_slice()
}
#[inline]
#[must_use]
pub fn fds(&self) -> &[RawFd] {
self.fds.as_slice()
}
#[inline]
#[must_use]
unsafe fn get_cur_byte_offset(&self, offset: isize) -> u32 {
let i = (self.cur as isize + offset) as usize;
unsafe { *self.bytes.get_unchecked(i) }
}
#[inline]
#[must_use]
unsafe fn get_cur_fd(&self) -> i32 {
unsafe { *self.fds.get_unchecked(self.cur_fd as usize) }
}
}
impl Receiver {
#[inline]
#[must_use]
pub(crate) fn new() -> Self {
Self {
payload: Payload::new(),
}
}
#[inline]
pub fn recv<'a>(
&'a mut self,
wayland_fd: &impl rustix::fd::AsFd,
) -> Result<Messages<'a>, Error> {
use rustix::{io, net};
let mut ancillary_buf = [MaybeUninit::uninit(); 32];
let iov = io::IoSliceMut::new(u32_slice_to_u8_mut(
&mut self.payload.bytes[self.payload.cur as usize..],
));
let mut control = net::RecvAncillaryBuffer::new(i32_slice_to_u8_mut(&mut ancillary_buf));
let bytes = net::recvmsg(
wayland_fd,
&mut [iov],
&mut control,
net::RecvFlags::empty(),
)
.map_err(Error::RustixIoErrno)?;
let mut fd_index = 0;
for msg in control.drain() {
if let net::RecvAncillaryMessage::ScmRights(iter) = msg {
let i = self.payload.cur_fd as usize;
for fd in iter {
*unsafe { self.payload.fds.get_unchecked_mut(i + fd_index) } = fd.into_raw_fd();
fd_index += 1;
}
}
}
let bytes_received = self.payload.cur + (bytes.bytes / core::mem::size_of::<u32>()) as u16;
let fds_received = self.payload.cur_fd + fd_index as u8;
self.payload.cur = 0;
self.payload.cur_fd = 0;
Ok(Messages {
sender_id: ObjectId::new(NonZeroU32::new(1).unwrap()),
op: 0,
bytes_received,
fds_received,
payload: self.payload.as_mut(),
})
}
#[inline]
#[must_use]
pub fn payload(&self) -> &Payload {
&self.payload
}
}
impl Default for Receiver {
fn default() -> Self {
Self::new()
}
}
impl Iterator for Messages<'_> {
type Item = Result<ObjectId, Error>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.payload.cur + 1 >= self.bytes_received {
return None;
}
let byte = unsafe { self.payload.get_cur_byte_offset(1) };
let len = byte >> 16;
if self.payload.cur + len as u16 / 4 > self.bytes_received {
return None;
}
match ObjectId::try_new(unsafe { self.payload.get_cur_byte_offset(0) }) {
Ok(sender_id) => {
self.sender_id = sender_id;
self.op = (byte & 0xFFFF) as u16;
self.payload.cur += 2;
Some(Ok(sender_id))
}
Err(_) => Some(Err(Error::NullObjectId)),
}
}
}
impl Messages<'_> {
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_fd(&mut self) -> OwnedFd {
let fd = unsafe { self.payload.get_cur_fd() };
self.payload.cur_fd += 1;
unsafe { OwnedFd::from_raw_fd(fd) }
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn sender_id(&self) -> ObjectId {
self.sender_id
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn op(&self) -> u16 {
self.op
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_i32(&mut self) -> i32 {
self.next_u32() as i32
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_u32(&mut self) -> u32 {
self.payload.cur += 1;
unsafe { self.payload.get_cur_byte_offset(-1) }
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_fixed(&mut self) -> WlFixed {
WlFixed(self.next_i32())
}
#[inline]
#[doc(hidden)]
pub fn next_string<'a>(&mut self) -> Result<&'a str, Error> {
let len = unsafe { self.payload.get_cur_byte_offset(0) as usize };
unsafe {
let ptr = self
.payload
.bytes()
.as_ptr()
.add(self.payload.cur as usize + 1);
let cast = core::slice::from_raw_parts(ptr.cast(), len - 1);
self.payload.cur += 1 + ((len + 3) >> 2) as u16;
core::str::from_utf8(cast).map_err(Error::InvalidUTF8FromWire)
}
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_object(&mut self) -> Option<ObjectId> {
ObjectId::try_new(self.next_u32()).ok()
}
#[inline]
#[doc(hidden)]
pub fn next_new_specified_id(&mut self) -> Result<ObjectId, Error> {
ObjectId::try_new(self.next_u32())
}
#[inline]
#[doc(hidden)]
pub fn next_new_unspecified_id<'a>(&mut self) -> Result<NewId<'a>, Error> {
let interface = self.next_string()?;
let version = self.next_u32();
let id = self.next_new_specified_id()?;
Ok(NewId {
id,
interface,
version,
})
}
#[inline]
#[must_use]
#[doc(hidden)]
pub fn next_array<'a>(&mut self) -> &'a [u8] {
let len = unsafe { self.payload.get_cur_byte_offset(0) as usize };
unsafe {
let ptr = self
.payload
.bytes()
.as_ptr()
.add(self.payload.cur as usize + 1); self.payload.cur += 1 + ((len + 3) >> 2) as u16;
core::slice::from_raw_parts(ptr.cast(), len)
}
}
}
impl Drop for Messages<'_> {
fn drop(&mut self) {
let remaining_byte_len = (self.bytes_received).saturating_sub(self.payload.cur);
let remaining_fds_len = (self.fds_received).saturating_sub(self.payload.cur_fd);
unsafe {
let (lo, hi) = self
.payload
.bytes
.split_at_mut_unchecked(self.payload.cur as usize);
lo.as_mut_ptr()
.copy_from(hi.as_ptr(), remaining_byte_len as usize)
}
unsafe {
let (lo, hi) = self
.payload
.fds
.split_at_mut_unchecked(self.payload.cur_fd as usize);
lo.as_mut_ptr()
.copy_from(hi.as_ptr(), remaining_fds_len as usize)
}
self.payload.cur = remaining_byte_len;
self.payload.cur_fd = remaining_fds_len;
}
}
struct MessageBuilderInner {
msg: WaylandBuffer,
fds: [RawFd; 32],
msg_len: u16,
fds_len: u8,
last_fully_encoded_msg_index: u16,
last_fully_encoded_fds_index: u8,
}
pub struct MessageBuilder(Box<MessageBuilderInner>);
impl MessageBuilderInner {
#[inline]
fn add_header<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
sender_id: ObjectId,
op: u16,
) -> Result<(), Error> {
self.add_u32(wayland_fd, sender_id.get().get())?;
self.add_u32(wayland_fd, op as u32)?;
Ok(())
}
#[inline]
fn add_i32<Fd: rustix::fd::AsFd>(&mut self, wayland_fd: &Fd, i: i32) -> Result<(), Error> {
self.add_u32(wayland_fd, i as u32)?;
Ok(())
}
#[inline]
fn add_u32<Fd: rustix::fd::AsFd>(&mut self, wayland_fd: &Fd, u: u32) -> Result<(), Error> {
if self.msg_len as usize >= self.msg.len() {
self.flush(wayland_fd)?;
}
self.msg[self.msg_len as usize] = u;
self.msg_len += 1;
Ok(())
}
#[inline]
fn add_fixed<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
fixed: WlFixed,
) -> Result<(), Error> {
self.add_i32(wayland_fd, fixed.0)?;
Ok(())
}
#[inline]
fn add_string<Fd: rustix::fd::AsFd>(&mut self, wayland_fd: &Fd, s: &str) -> Result<(), Error> {
let str = WlStr(s);
if 1 + (str.0.len() + 1).div_ceil(4) + self.msg_len as usize >= self.msg.len() {
self.flush(wayland_fd)?;
}
let bytes = str.encode(&mut self.msg[self.msg_len as usize..]);
self.msg_len += (bytes >> 2) as u16;
Ok(())
}
#[inline]
fn add_object<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
object_id: Option<ObjectId>,
) -> Result<(), Error> {
match object_id {
Some(id) => self.add_u32(wayland_fd, id.get().get())?,
None => self.add_u32(wayland_fd, 0)?,
}
Ok(())
}
#[inline]
fn add_new_specified_id<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
object_id: ObjectId,
) -> Result<(), Error> {
self.add_u32(wayland_fd, object_id.get().get())?;
Ok(())
}
#[inline]
fn add_new_unspecified_id<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
object_id: ObjectId,
interface: &str,
version: u32,
) -> Result<(), Error> {
self.add_string(wayland_fd, interface)?;
self.add_u32(wayland_fd, version)?;
self.add_new_specified_id(wayland_fd, object_id)?;
Ok(())
}
#[inline]
fn add_array<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
array: &[u8],
) -> Result<(), Error> {
let slice = WlSlice(array);
if 1 + slice.0.len().div_ceil(4) + self.msg_len as usize >= self.msg.len() {
self.flush(wayland_fd)?;
}
let bytes = slice.encode(&mut self.msg[self.msg_len as usize..]);
self.msg_len += (bytes >> 2) as u16;
Ok(())
}
#[inline]
fn add_fd<'a, 'b, Fd>(&'a mut self, wayland_fd: &Fd, fd: &'b impl AsRawFd) -> Result<(), Error>
where
Fd: rustix::fd::AsFd,
'b: 'a,
{
if self.fds_len as usize >= self.fds.len() {
self.flush(wayland_fd)?;
}
self.fds[self.fds_len as usize] = fd.as_raw_fd();
self.fds_len += 1;
Ok(())
}
#[inline]
fn finish(&mut self) {
let len = ((self.msg_len - self.last_fully_encoded_msg_index) as u32) << 2;
*unsafe {
self.msg
.get_unchecked_mut(self.last_fully_encoded_msg_index as usize + 1)
} |= len << 16;
self.last_fully_encoded_msg_index = self.msg_len;
self.last_fully_encoded_fds_index = self.fds_len;
}
fn flush<Fd: rustix::fd::AsFd>(&mut self, wayland_fd: &Fd) -> Result<(), Error> {
use rustix::{io, net};
const FLAGS: net::SendFlags = net::SendFlags::NOSIGNAL;
let Self {
msg,
fds,
msg_len,
fds_len,
last_fully_encoded_msg_index,
last_fully_encoded_fds_index,
} = self;
let msg_slice = u32_slice_to_u8(unsafe {
msg.get_unchecked(0..*last_fully_encoded_msg_index as usize)
});
if *fds_len == 0 {
net::send(wayland_fd, msg_slice, FLAGS).map_err(Error::RustixIoErrno)?;
} else {
let iov = io::IoSlice::new(msg_slice);
let mut space = [MaybeUninit::<u8>::uninit(); rustix::cmsg_space!(ScmRights(32))];
let mut control = net::SendAncillaryBuffer::new(&mut space);
control.push(net::SendAncillaryMessage::ScmRights(unsafe {
core::mem::transmute::<&[i32], &[rustix::fd::BorrowedFd<'_>]>(
fds.get_unchecked(..*fds_len as usize),
)
}));
net::sendmsg(wayland_fd, &[iov], &mut control, FLAGS).map_err(Error::RustixIoErrno)?;
}
unsafe {
let (lo, hi) = msg.split_at_mut_unchecked(*last_fully_encoded_msg_index as usize);
let len = (*msg_len - *last_fully_encoded_msg_index) as usize;
lo.as_mut_ptr().copy_from(hi.as_ptr(), len);
let (lo, hi) = fds.split_at_mut_unchecked(*last_fully_encoded_fds_index as usize);
let len = (*fds_len - *last_fully_encoded_fds_index) as usize;
lo.as_mut_ptr().copy_from(hi.as_ptr(), len);
}
*msg_len -= *last_fully_encoded_msg_index;
*fds_len -= *last_fully_encoded_fds_index;
*last_fully_encoded_fds_index = 0;
*last_fully_encoded_msg_index = 0;
Ok(())
}
}
impl MessageBuilder {
#[inline]
#[must_use]
pub(crate) fn new() -> Self {
unsafe {
let layout = Layout::new::<MessageBuilderInner>();
let ptr = alloc::alloc_zeroed(layout);
if ptr.is_null() {
alloc::handle_alloc_error(layout);
}
Self(Box::from_raw(ptr.cast()))
}
}
#[inline]
#[doc(hidden)]
pub fn add_header<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
sender_id: ObjectId,
op: u16,
) -> Result<(), Error> {
self.0.add_header(wayland_fd, sender_id, op)
}
#[inline]
#[doc(hidden)]
pub fn add_i32<Fd: rustix::fd::AsFd>(&mut self, wayland_fd: &Fd, i: i32) -> Result<(), Error> {
self.0.add_i32(wayland_fd, i)
}
#[inline]
#[doc(hidden)]
pub fn add_u32<Fd: rustix::fd::AsFd>(&mut self, wayland_fd: &Fd, u: u32) -> Result<(), Error> {
self.0.add_u32(wayland_fd, u)
}
#[inline]
#[doc(hidden)]
pub fn add_fixed<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
fixed: WlFixed,
) -> Result<(), Error> {
self.0.add_fixed(wayland_fd, fixed)
}
#[inline]
#[doc(hidden)]
pub fn add_string<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
s: &str,
) -> Result<(), Error> {
self.0.add_string(wayland_fd, s)
}
#[inline]
#[doc(hidden)]
pub fn add_object<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
object_id: Option<ObjectId>,
) -> Result<(), Error> {
self.0.add_object(wayland_fd, object_id)
}
#[inline]
#[doc(hidden)]
pub fn add_new_specified_id<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
object_id: ObjectId,
) -> Result<(), Error> {
self.0.add_new_specified_id(wayland_fd, object_id)
}
#[inline]
#[doc(hidden)]
pub fn add_new_unspecified_id<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
object_id: ObjectId,
interface: &str,
version: u32,
) -> Result<(), Error> {
self.0
.add_new_unspecified_id(wayland_fd, object_id, interface, version)
}
#[inline]
#[doc(hidden)]
pub fn add_array<Fd: rustix::fd::AsFd>(
&mut self,
wayland_fd: &Fd,
array: &[u8],
) -> Result<(), Error> {
self.0.add_array(wayland_fd, array)
}
#[inline]
#[doc(hidden)]
pub fn add_fd<'a, 'b, Fd>(
&'a mut self,
wayland_fd: &Fd,
fd: &'b impl AsRawFd,
) -> Result<(), Error>
where
Fd: rustix::fd::AsFd,
'b: 'a,
{
self.0.add_fd(wayland_fd, fd)
}
#[inline]
#[doc(hidden)]
pub fn finish(&mut self) {
self.0.finish()
}
#[inline]
#[doc(hidden)]
pub fn flush<Fd: rustix::fd::AsFd>(&mut self, wayland_fd: &Fd) -> Result<(), Error> {
self.0.flush(wayland_fd)
}
}
impl Default for MessageBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub enum Error {
UnrecognizedEventOpCode((&'static str, u16)),
InvalidEnumDiscriminant((&'static str, u32)),
RustixIoErrno(rustix::io::Errno),
InvalidUTF8FromWire(core::str::Utf8Error),
NullObjectId,
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Error::UnrecognizedEventOpCode((event, opcode)) => {
write!(f, "unrecognized opcode {opcode} for event {event}")
}
Error::InvalidEnumDiscriminant((enum_name, number)) => {
write!(f, "invalid discriminant {number} for enum {enum_name}")
}
Error::RustixIoErrno(errno) => write!(f, "{errno}"),
Error::InvalidUTF8FromWire(utf8_error) => write!(
f,
"received invalid utf8 string from the wire: {utf8_error}"
),
Error::NullObjectId => write!(f, "received unexpected null ObjectId"),
}
}
}
impl core::error::Error for Error {}
#[cfg(test)]
mod tests {
extern crate std;
use core::{ffi::c_int, mem::ManuallyDrop, num::NonZeroU32};
use std::{path::Path, string::ToString};
use tempdir::TempDir;
use super::*;
const DUMMY_FD: ManuallyDrop<OwnedFd> =
unsafe { ManuallyDrop::new(std::mem::transmute::<i32, OwnedFd>(c_int::MAX)) };
const DUMMY_OBJ: ObjectId = ObjectId::new(NonZeroU32::new(1).unwrap());
fn bind_dummy_wayland_socket<P: AsRef<Path>>(tmp_dir: &TempDir, path: P) -> OwnedFd {
let path = tmp_dir.path().join(path);
let unix_addr =
rustix::net::SocketAddrUnix::new(path.display().to_string().as_str()).unwrap();
let socket = rustix::net::socket_with(
rustix::net::AddressFamily::UNIX,
rustix::net::SocketType::STREAM,
rustix::net::SocketFlags::CLOEXEC,
None,
)
.unwrap();
rustix::net::bind(&socket, &unix_addr).unwrap();
rustix::net::listen(&socket, 0).unwrap();
socket
}
fn connect_dummy_wayland_socket<P: AsRef<Path>>(tmp_dir: &TempDir, path: P) -> OwnedFd {
let path = tmp_dir.path().join(path);
let stream = std::os::unix::net::UnixStream::connect(path).unwrap();
stream.set_nonblocking(true).unwrap();
let fd = std::os::fd::IntoRawFd::into_raw_fd(stream);
unsafe { OwnedFd::from_raw_fd(fd) }
}
#[test]
fn msg_builder_header() {
let mut builder = MessageBuilder::new();
builder.add_header(&*DUMMY_FD, DUMMY_OBJ, 0).unwrap();
assert_eq!(
&builder.0.msg[..builder.0.msg_len as usize],
&[DUMMY_OBJ.get().get(), 0]
);
builder.finish();
assert_eq!(
&builder.0.msg[..builder.0.msg_len as usize],
&[DUMMY_OBJ.get().get(), 8 << 16]
);
builder.add_header(&*DUMMY_FD, DUMMY_OBJ, 1).unwrap();
assert_eq!(
&builder.0.msg[..builder.0.msg_len as usize],
&[DUMMY_OBJ.get().get(), 8 << 16, DUMMY_OBJ.get().get(), 1]
);
builder.finish();
assert_eq!(
&builder.0.msg[..builder.0.msg_len as usize],
&[
DUMMY_OBJ.get().get(),
8 << 16,
DUMMY_OBJ.get().get(),
1 | (8 << 16)
]
);
}
#[test]
fn msg_builder_arrays() {
let mut builder = MessageBuilder::new();
builder.add_header(&*DUMMY_FD, DUMMY_OBJ, 1).unwrap();
builder.add_array(&*DUMMY_FD, &[]).unwrap();
builder.add_array(&*DUMMY_FD, &[1]).unwrap();
builder.add_array(&*DUMMY_FD, &[1, 2]).unwrap();
builder.add_array(&*DUMMY_FD, &[1, 2, 3]).unwrap();
builder.add_array(&*DUMMY_FD, &[1, 2, 3, 4]).unwrap();
builder.finish();
assert_eq!(
&builder.0.msg[..builder.0.msg_len as usize],
&[
DUMMY_OBJ.get().get(),
1 | ((8 + 4 + 8 + 8 + 8 + 8) << 16),
0, 1, u32::from_ne_bytes([1, 0, 0, 0]),
2, u32::from_ne_bytes([1, 2, 0, 0]),
3, u32::from_ne_bytes([1, 2, 3, 0]),
4, u32::from_ne_bytes([1, 2, 3, 4]),
]
);
}
#[test]
fn msg_builder_strings() {
let mut builder = MessageBuilder::new();
builder.add_header(&*DUMMY_FD, DUMMY_OBJ, 1).unwrap();
builder.add_string(&*DUMMY_FD, "").unwrap();
builder.add_string(&*DUMMY_FD, "1").unwrap();
builder.add_string(&*DUMMY_FD, "12").unwrap();
builder.add_string(&*DUMMY_FD, "123").unwrap();
builder.add_string(&*DUMMY_FD, "1234").unwrap();
builder.finish();
assert_eq!(
&builder.0.msg[..builder.0.msg_len as usize],
&[
DUMMY_OBJ.get().get(),
1 | ((8 + 8 + 8 + 8 + 8 + 12) << 16),
1, 0,
2, u32::from_ne_bytes([b'1', 0, 0, 0]),
3, u32::from_ne_bytes([b'1', b'2', 0, 0]),
4, u32::from_ne_bytes([b'1', b'2', b'3', 0]),
5, u32::from_ne_bytes([b'1', b'2', b'3', b'4']),
0,
]
);
}
#[test]
fn msg_builder_scalars() {
let mut builder = MessageBuilder::new();
builder.add_header(&*DUMMY_FD, DUMMY_OBJ, 1).unwrap();
builder.add_u32(&*DUMMY_FD, 1).unwrap();
builder.add_i32(&*DUMMY_FD, 2).unwrap();
builder.add_i32(&*DUMMY_FD, -1).unwrap();
builder.add_fixed(&*DUMMY_FD, WlFixed::from(1i32)).unwrap();
builder
.add_object(&*DUMMY_FD, Some(ObjectId::new(10.try_into().unwrap())))
.unwrap();
builder.add_fd(&*DUMMY_FD, &*DUMMY_FD).unwrap();
builder.finish();
assert_eq!(
&builder.0.msg[..builder.0.msg_len as usize],
&[
DUMMY_OBJ.get().get(),
1 | ((8 + 4 + 4 + 4 + 4 + 4) << 16),
1,
2,
-1i32 as u32,
WlFixed::from(1i32).0 as u32,
ObjectId::new(10.try_into().unwrap()).get().get()
]
);
assert_eq!(
&builder.0.fds[..builder.0.fds_len as usize],
&[DUMMY_FD.as_raw_fd()]
)
}
#[test]
fn overflowing_msg() {
let tmpdir = TempDir::new("test_overflowing_msg").unwrap();
let arr = std::vec![0u8; u16::MAX as usize - 14];
let receiver_fd = bind_dummy_wayland_socket(&tmpdir, "tmp");
{
let sender_fd = connect_dummy_wayland_socket(&tmpdir, "tmp");
let mut builder = MessageBuilder::new();
builder.add_header(&sender_fd, DUMMY_OBJ, 0).unwrap();
builder.add_array(&sender_fd, &arr).unwrap();
builder.finish();
assert_eq!(builder.0.msg_len as usize, builder.0.msg.len());
builder.add_header(&sender_fd, DUMMY_OBJ, 1).unwrap();
builder.finish();
assert_eq!(builder.0.msg_len, 2);
assert_eq!(
&builder.0.msg[0..builder.0.msg_len as usize],
&[DUMMY_OBJ.get().get(), 1 | (8 << 16)]
);
builder.flush(&sender_fd).unwrap();
}
{
let fd = rustix::net::accept(receiver_fd).unwrap();
let mut receiver = Receiver::new();
{
let mut msg = receiver.recv(&fd).unwrap();
assert!(msg.next().unwrap().is_ok());
let expected = &arr;
let array = msg.next_array();
assert_eq!(array.len(), expected.len());
assert_eq!(array, expected);
}
assert_eq!(receiver.payload.cur, 0);
let mut msg = receiver.recv(&fd).unwrap();
assert!(msg.next().unwrap().is_ok());
assert_eq!(
&msg.payload.bytes()[..2],
&[DUMMY_OBJ.get().get(), 1 | (8 << 16)]
);
}
}
#[test]
fn receiving_incomplete_messages() {
let tmpdir = TempDir::new("test_receiving_incomplete_messages").unwrap();
let receiver_fd = bind_dummy_wayland_socket(&tmpdir, "tmp");
let mut receiver = Receiver::new();
{
let sender_fd = connect_dummy_wayland_socket(&tmpdir, "tmp");
rustix::net::send(
&sender_fd,
u32_slice_to_u8(&[1, 12 << 16]),
rustix::net::SendFlags::empty(),
)
.unwrap();
}
{
{
let fd = rustix::net::accept(&receiver_fd).unwrap();
let mut msg = receiver.recv(&fd).unwrap();
assert!(msg.next().is_none());
}
assert_eq!(receiver.payload.cur_fd, 0);
assert_eq!(receiver.payload.cur, 2);
}
{
let sender_fd = connect_dummy_wayland_socket(&tmpdir, "tmp");
rustix::net::send(
&sender_fd,
u32_slice_to_u8(&[2]),
rustix::net::SendFlags::empty(),
)
.unwrap();
}
{
let fd = rustix::net::accept(&receiver_fd).unwrap();
let mut msg = receiver.recv(&fd).unwrap();
assert!(msg.next().unwrap().is_ok());
let obj = msg.sender_id();
let op = msg.op();
let value = msg.next_u32();
assert_eq!(obj, ObjectId::new(NonZeroU32::new(1).unwrap()));
assert_eq!(op, 0);
assert_eq!(value, 2);
assert!(msg.next().is_none());
}
assert_eq!(receiver.payload.cur_fd, 0);
assert_eq!(receiver.payload.cur, 0);
}
#[test]
fn receiving_multiple_messages() {
let tmpdir = TempDir::new("test_receiving_multiple_messages").unwrap();
let receiver_fd = bind_dummy_wayland_socket(&tmpdir, "tmp");
{
let sender_fd = connect_dummy_wayland_socket(&tmpdir, "tmp");
rustix::net::send(
&sender_fd,
u32_slice_to_u8(&[1, 12 << 16, 2, 3, 12 << 16, 4]),
rustix::net::SendFlags::empty(),
)
.unwrap();
}
let mut receiver = Receiver::new();
{
let fd = rustix::net::accept(&receiver_fd).unwrap();
let mut msg = receiver.recv(&fd).unwrap();
assert!(msg.next().unwrap().is_ok());
let obj = msg.sender_id();
let op = msg.op();
let value = msg.next_u32();
assert_eq!(obj, ObjectId::new(NonZeroU32::new(1).unwrap()));
assert_eq!(op, 0);
assert_eq!(value, 2);
assert!(msg.next().unwrap().is_ok());
let obj = msg.sender_id();
let op = msg.op();
let value = msg.next_u32();
assert_eq!(obj, ObjectId::new(NonZeroU32::new(3).unwrap()));
assert_eq!(op, 0);
assert_eq!(value, 4);
}
assert_eq!(receiver.payload.cur_fd, 0);
assert_eq!(receiver.payload.cur, 0);
}
}