From 259a9fb892e777832d6d0cc0293584ffcefcdd93 Mon Sep 17 00:00:00 2001 From: rkuklik Date: Wed, 26 Jun 2024 14:18:11 +0200 Subject: [PATCH 1/4] refactor(mmap): switch to pointer to slice --- common/src/mmap.rs | 104 +++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 56 deletions(-) diff --git a/common/src/mmap.rs b/common/src/mmap.rs index 87696fa..a279e30 100644 --- a/common/src/mmap.rs +++ b/common/src/mmap.rs @@ -1,4 +1,5 @@ use std::iter::repeat_with; +use std::ptr; use std::ptr::NonNull; use std::time::SystemTime; use std::time::UNIX_EPOCH; @@ -20,8 +21,7 @@ use rustix::shm::ShmOFlags; #[derive(Debug)] pub struct Mmap { fd: OwnedFd, - ptr: NonNull, - len: usize, + buffer: NonNull<[u8]>, mmaped: bool, } @@ -33,18 +33,17 @@ impl Mmap { #[must_use] pub fn create(len: usize) -> Self { let fd = Self::mmap_fd().unwrap(); - rustix::io::retry_on_intr(|| rustix::fs::ftruncate(&fd, len as u64)).unwrap(); + io::retry_on_intr(|| fs::ftruncate(&fd, len as u64)).unwrap(); - let ptr = unsafe { - let ptr = mmap(std::ptr::null_mut(), len, Self::PROT, Self::FLAGS, &fd, 0).unwrap(); - // SAFETY: the function above will never return a null pointer if it succeeds - // POSIX says that the implementation will never select an address at 0 - NonNull::new_unchecked(ptr) + // SAFETY: the function above will never return a null pointer if it succeeds + // POSIX says that the implementation will never select an address at 0 + let buffer = unsafe { + let ptr = mmap(ptr::null_mut(), len, Self::PROT, Self::FLAGS, &fd, 0).unwrap(); + NonNull::slice_from_raw_parts(NonNull::new_unchecked(ptr.cast()), len) }; Self { fd, - ptr, - len, + buffer, mmaped: true, } } @@ -109,7 +108,7 @@ impl Mmap { /// /// This is only ever used in the daemon, when animations finish, in order to free up memory pub fn unmap(&mut self) { - if let Err(e) = unsafe { munmap(self.ptr.as_ptr(), self.len) } { + if let Err(e) = unsafe { munmap(self.buffer.as_ptr().cast(), self.len()) } { eprintln!("ERROR WHEN UNMAPPING MEMORY: {e}"); } else { self.mmaped = false; @@ -123,19 +122,20 @@ impl Mmap { pub fn ensure_mapped(&mut self) { if !self.mmaped { self.mmaped = true; - self.ptr = unsafe { + + // SAFETY: the function above will never return a null pointer if it succeeds + // POSIX says that the implementation will never select an address at 0 + self.buffer = unsafe { let ptr = mmap( - std::ptr::null_mut(), - self.len, + ptr::null_mut(), + self.len(), Self::PROT, Self::FLAGS, &self.fd, 0, ) .unwrap(); - // SAFETY: the function above will never return a null pointer if it succeeds - // POSIX says that the implementation will never select an address at 0 - NonNull::new_unchecked(ptr) + NonNull::slice_from_raw_parts(NonNull::new_unchecked(ptr.cast()), self.len()) }; } } @@ -148,57 +148,45 @@ impl Mmap { { use rustix::mm; - let result = - unsafe { mm::mremap(self.ptr.as_ptr(), self.len, new, mm::MremapFlags::MAYMOVE) }; + let result = unsafe { + mm::mremap( + self.buffer.as_ptr().cast(), + self.len(), + new, + mm::MremapFlags::MAYMOVE, + ) + }; if let Ok(ptr) = result { // SAFETY: the mremap above will never return a null pointer if it succeeds - let ptr = unsafe { NonNull::new_unchecked(ptr) }; - self.ptr = ptr; - self.len = new; + self.buffer = unsafe { + NonNull::slice_from_raw_parts(NonNull::new_unchecked(ptr.cast()), new) + }; return; } } self.unmap(); - self.len = new; - self.ptr = unsafe { - let ptr = mmap( - std::ptr::null_mut(), - self.len, - Self::PROT, - Self::FLAGS, - &self.fd, - 0, - ) - .unwrap(); - // SAFETY: the function above will never return a null pointer if it succeeds - // POSIX says that the implementation will never select an address at 0 - NonNull::new_unchecked(ptr) + // SAFETY: the function above will never return a null pointer if it succeeds + // POSIX says that the implementation will never select an address at 0 + self.buffer = unsafe { + let ptr = mmap(ptr::null_mut(), new, Self::PROT, Self::FLAGS, &self.fd, 0).unwrap(); + NonNull::slice_from_raw_parts(NonNull::new_unchecked(ptr.cast()), new) }; } #[must_use] pub(crate) fn from_fd(fd: OwnedFd, len: usize) -> Self { - let ptr = unsafe { - let ptr = mmap( - std::ptr::null_mut(), - len, - ProtFlags::READ, - Self::FLAGS, - &fd, - 0, - ) - .unwrap(); - // SAFETY: the function above will never return a null pointer if it succeeds - // POSIX says that the implementation will never select an address at 0 - NonNull::new_unchecked(ptr) + // SAFETY: the function above will never return a null pointer if it succeeds + // POSIX says that the implementation will never select an address at 0 + let buffer = unsafe { + let ptr = mmap(ptr::null_mut(), len, Self::PROT, Self::FLAGS, &fd, 0).unwrap(); + NonNull::slice_from_raw_parts(NonNull::new_unchecked(ptr.cast()), len) }; Self { fd, - ptr, - len, + buffer, mmaped: true, } } @@ -206,20 +194,24 @@ impl Mmap { #[inline] #[must_use] pub fn slice_mut(&mut self) -> &mut [u8] { - unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr().cast(), self.len) } + unsafe { self.buffer.as_mut() } } #[inline] #[must_use] pub fn slice(&self) -> &[u8] { - unsafe { std::slice::from_raw_parts(self.ptr.as_ptr().cast(), self.len) } + unsafe { self.buffer.as_ref() } } #[inline] #[must_use] - #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { - self.len + self.buffer.len() + } + + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 } #[inline] @@ -258,7 +250,7 @@ impl Mmapped { } pub(crate) fn new_with_len(map: &Mmap, bytes: &[u8], len: usize) -> Self { - let offset = bytes.as_ptr() as usize - map.ptr.as_ptr() as usize; + let offset = bytes.as_ptr() as usize - map.buffer.as_ptr() as *mut u8 as usize; let page_size = rustix::param::page_size(); let page_offset = offset - offset % page_size; From 670e78561af21c39af31a469d49f4c76efce903a Mon Sep 17 00:00:00 2001 From: rkuklik Date: Wed, 26 Jun 2024 14:19:39 +0200 Subject: [PATCH 2/4] refactor(ipc): sketch out new types --- common/src/ipc/mod.rs | 2 + common/src/ipc/types2.rs | 113 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 common/src/ipc/types2.rs diff --git a/common/src/ipc/mod.rs b/common/src/ipc/mod.rs index 6565261..24d2c38 100644 --- a/common/src/ipc/mod.rs +++ b/common/src/ipc/mod.rs @@ -1,11 +1,13 @@ use std::path::PathBuf; use transmit::RawMsg; +use transmit::IpcMessage; mod error; mod socket; mod transmit; mod types; +pub mod types2; use crate::cache; use crate::mmap::Mmap; diff --git a/common/src/ipc/types2.rs b/common/src/ipc/types2.rs new file mode 100644 index 0000000..a289198 --- /dev/null +++ b/common/src/ipc/types2.rs @@ -0,0 +1,113 @@ +use std::num::NonZeroI32; +use std::num::NonZeroU8; +use std::time::Duration; + +pub struct Vec2 { + pub x: T, + pub y: T, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum PixelFormat { + /// No swap, can copy directly onto WlBuffer + Bgr = 0, + /// Swap R and B channels at client, can copy directly onto WlBuffer + Rgb = 1, + /// No swap, must extend pixel with an extra byte when copying + Xbgr = 2, + /// Swap R and B channels at client, must extend pixel with an extra byte when copying + Xrgb = 3, +} + +pub struct Image<'a> { + pub dim: Vec2, + pub format: PixelFormat, + pub img: &'a [u8], +} + +#[repr(u8)] +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum TransitionType { + Simple = 0, + Fade = 1, + Outer = 2, + Wipe = 3, + Grow = 4, + Wave = 5, + None = 6, +} + +#[derive(Clone, PartialEq)] +pub enum Coord { + Pixel(f32), + Percent(f32), +} + +pub struct Transition { + pub transition_type: TransitionType, + pub duration: f32, + pub step: NonZeroU8, + pub fps: u16, + pub angle: f64, + pub pos: Vec2, + pub bezier: (Vec2, Vec2), + pub wave: Vec2, + pub invert_y: bool, +} + +pub struct BitPack<'a> { + pub bytes: &'a [u8], + pub expected_size: u32, +} + +pub struct Animation<'a> { + pub animation: Box<[(BitPack<'a>, Duration)]>, +} + +pub struct ImageRequest<'a> { + pub transition: Transition, + pub imgs: Box<[Image<'a>]>, + pub outputs: Box<[&'a str]>, + pub animations: Option]>>, +} + +pub struct ClearRequest<'a> { + pub color: [u8; 3], + pub outputs: Box<[&'a str]>, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Scale { + Whole(NonZeroI32), + Fractional(NonZeroI32), +} + +#[derive(Debug, PartialEq, Clone)] +pub enum ImageDescription { + Color([u8; 3]), + Img(String), +} + +pub struct Info { + pub name: String, + pub dim: Vec2, + pub scale: Scale, + pub img: ImageDescription, + pub format: PixelFormat, +} + +pub enum Request<'a> { + Ping, + Query, + Clear(ClearRequest<'a>), + Img(ImageRequest<'a>), + Kill, +} + +// TODO: perhaps add error propagation +pub enum Response { + Ok, + Ping(bool), + Info(Box<[Info]>), +} From 6a0da32ebbf575022bacdd4df4e9c8b6ba74318a Mon Sep 17 00:00:00 2001 From: rkuklik Date: Wed, 26 Jun 2024 14:20:34 +0200 Subject: [PATCH 3/4] feat(serde): prepare unified serialization interface --- common/src/ipc/mod.rs | 6 +- common/src/ipc/serde.rs | 631 +++++++++++++++++++++++++++++++++++++ common/src/ipc/transmit.rs | 188 +++++++++-- 3 files changed, 799 insertions(+), 26 deletions(-) create mode 100644 common/src/ipc/serde.rs diff --git a/common/src/ipc/mod.rs b/common/src/ipc/mod.rs index 24d2c38..1064e37 100644 --- a/common/src/ipc/mod.rs +++ b/common/src/ipc/mod.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; -use transmit::RawMsg; use transmit::IpcMessage; mod error; +pub mod serde; mod socket; mod transmit; mod types; @@ -150,7 +150,7 @@ impl RequestSend { impl RequestRecv { #[must_use] #[inline] - pub fn receive(msg: RawMsg) -> Self { + pub fn receive(msg: IpcMessage) -> Self { msg.into() } } @@ -172,7 +172,7 @@ impl Answer { #[must_use] #[inline] - pub fn receive(msg: RawMsg) -> Self { + pub fn receive(msg: IpcMessage) -> Self { msg.into() } } diff --git a/common/src/ipc/serde.rs b/common/src/ipc/serde.rs new file mode 100644 index 0000000..46f5bfc --- /dev/null +++ b/common/src/ipc/serde.rs @@ -0,0 +1,631 @@ +use std::mem::size_of; +use std::mem::size_of_val; +use std::num::NonZeroI32; +use std::num::NonZeroU8; +use std::time::Duration; + +use super::types2::Animation; +use super::types2::BitPack; +use super::types2::ClearRequest; +use super::types2::Coord; +use super::types2::Image; +use super::types2::ImageDescription; +use super::types2::ImageRequest; +use super::types2::Info; +use super::types2::PixelFormat; +use super::types2::Scale; +use super::types2::Transition; +use super::types2::TransitionType; +use super::types2::Vec2; + +/// Type for managing access in [`Serialize`] and [`Deserialize`] +pub struct Cursor { + buf: T, + pos: usize, +} + +impl Cursor { + /// Create [`Self`] from provided buffer + pub fn new(buf: T) -> Self { + Self { buf, pos: 0 } + } + + /// Extract buffer back, discarding all internal state. + pub fn finish(self) -> T { + self.buf + } +} + +impl<'a> Cursor<&'a mut [u8]> { + fn write(&mut self, bytes: &[u8]) { + let next = self.pos + bytes.len(); + self.buf[self.pos..next].copy_from_slice(bytes); + self.pos = next; + } + + fn write_tagged(&mut self, bytes: &[u8]) { + bytes.tag().serialize(self); + self.write(bytes); + } +} + +impl<'a> Cursor<&'a [u8]> { + fn read(&mut self, count: usize) -> &'a [u8] { + let read = &self.buf[self.pos..self.pos + count]; + self.pos += count; + read + } + + fn read_tagged(&mut self) -> &'a [u8] { + let count = u32::deserialize(self); + self.read(count as usize) + } +} + +trait Tagged<'a> { + fn tag(&self) -> u32; +} + +impl Tagged<'_> for [T] { + fn tag(&self) -> u32 { + self.len() as u32 + } +} + +impl Tagged<'_> for str { + fn tag(&self) -> u32 { + self.as_bytes().tag() + } +} + +/// Serializes data structure into byte slice. +pub trait Serialize { + /// Write self into buffer. + /// + /// # Panics + /// + /// If [`size`](Serialize::size) is incorrectly implemented, buffer can be + /// too small to fit all bytes. + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>); + + /// Calculate resulting size of serialized data structure. + /// + /// Resulting value will be used to allocate buffer, so incorrect implementation + /// will either result in panic after out of bounds access or wasted space. + fn size(&self) -> usize; + + /// Same as [`size`](Serialize::size), but with space for slice length (now [`u32`]) + #[doc(hidden)] + fn size_tagged(&self) -> usize { + size_of::() + self.size() + } +} + +/// Deserializes byte slice into desired data structure. +pub trait Deserialize<'a> { + /// Read bytes from buffer and interpret as desired. + /// + /// # Panics + /// + /// If provided buffer is messed up (i.e. it isn't filled by corresponding + /// [`Serialize`] implementation). + /// + /// # Safety + /// + /// This function isn't marked as unsafe, so in no circumstances can + /// implementations count on provided buffer to be well formed. Panic instead. + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self; +} + +macro_rules! primitive { + ($($type:ty),+ $(,)?) => { + $( + impl Serialize for $type { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + self.to_ne_bytes().serialize(buffer) + } + + fn size(&self) -> usize { + size_of::<$type>() + } + } + + impl Deserialize<'_> for $type { + fn deserialize(buffer: &mut Cursor<&[u8]>) -> Self { + let array = buffer.read(size_of::<$type>()).try_into().expect("slice is correctly sized"); + Self::from_ne_bytes(array) + } + } + )+ + }; +} + +primitive!(u8, u16, u32, u64, u128, usize); +primitive!(i8, i16, i32, i64, i128, isize); +primitive!(f32, f64); + +impl Serialize for [u8] { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + buffer.write(self); + } + + fn size(&self) -> usize { + size_of_val(self) + } +} + +impl Serialize for str { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + self.as_bytes().serialize(buffer) + } + + fn size(&self) -> usize { + size_of_val(self) + } +} + +impl Serialize for Vec2 { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + self.x.serialize(buffer); + self.y.serialize(buffer); + } + + fn size(&self) -> usize { + 2 * size_of::() + } +} + +impl Serialize for Box<[T]> { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + self.tag().serialize(buffer); + for item in self.iter() { + item.serialize(buffer); + } + } + + fn size(&self) -> usize { + self.tag().size() + self.iter().map(T::size).sum::() + } +} + +impl<'a, T: Deserialize<'a>> Deserialize<'a> for Box<[T]> { + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { + (0..u32::deserialize(buffer)) + .map(|_| T::deserialize(buffer)) + .collect() + } +} + +impl<'a, T: Deserialize<'a>> Deserialize<'a> for Vec2 { + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { + let x = T::deserialize(buffer); + let y = T::deserialize(buffer); + Self { x, y } + } +} + +impl Serialize for PixelFormat { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + (*self as u8).serialize(buffer) + } + + fn size(&self) -> usize { + size_of_val(self) + } +} + +impl Deserialize<'_> for PixelFormat { + fn deserialize(buffer: &mut Cursor<&[u8]>) -> Self { + match u8::deserialize(buffer) { + num if num == Self::Bgr as u8 => Self::Bgr, + num if num == Self::Rgb as u8 => Self::Rgb, + num if num == Self::Xbgr as u8 => Self::Xbgr, + num if num == Self::Xrgb as u8 => Self::Xrgb, + _ => unreachable!("invalid discriminant"), + } + } +} + +impl Serialize for Image<'_> { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + self.dim.serialize(buffer); + self.format.serialize(buffer); + buffer.write_tagged(self.img); + } + + fn size(&self) -> usize { + self.dim.size() + self.format.size() + self.img.size_tagged() + } +} + +impl<'a> Deserialize<'a> for Image<'a> { + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { + let dim = Vec2::::deserialize(buffer); + let format = PixelFormat::deserialize(buffer); + let img = buffer.read_tagged(); + Self { dim, format, img } + } +} + +impl Serialize for TransitionType { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + (*self as u8).serialize(buffer) + } + + fn size(&self) -> usize { + size_of_val(self) + } +} + +impl Deserialize<'_> for TransitionType { + fn deserialize(buffer: &mut Cursor<&[u8]>) -> Self { + match u8::deserialize(buffer) { + num if num == Self::Simple as u8 => Self::Simple, + num if num == Self::Fade as u8 => Self::Fade, + num if num == Self::Outer as u8 => Self::Outer, + num if num == Self::Wipe as u8 => Self::Wipe, + num if num == Self::Grow as u8 => Self::Grow, + num if num == Self::Wave as u8 => Self::Wave, + num if num == Self::None as u8 => Self::None, + _ => unreachable!("invalid discriminant"), + } + } +} + +impl Serialize for Coord { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + match self { + Self::Pixel(pixel) => { + 0u8.serialize(buffer); + pixel.serialize(buffer); + } + Self::Percent(percent) => { + 1u8.serialize(buffer); + percent.serialize(buffer); + } + } + } + + fn size(&self) -> usize { + size_of_val(self) + } +} + +impl Deserialize<'_> for Coord { + fn deserialize(buffer: &mut Cursor<&[u8]>) -> Self { + match u8::deserialize(buffer) { + 0u8 => Self::Pixel(f32::deserialize(buffer)), + 1u8 => Self::Percent(f32::deserialize(buffer)), + _ => unreachable!("invalid discriminant"), + } + } +} + +impl Serialize for Transition { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + self.transition_type.serialize(buffer); + self.duration.serialize(buffer); + self.step.get().serialize(buffer); + self.fps.serialize(buffer); + self.angle.serialize(buffer); + self.pos.serialize(buffer); + self.bezier.0.serialize(buffer); + self.bezier.1.serialize(buffer); + self.wave.serialize(buffer); + u8::from(self.invert_y).serialize(buffer); + } + + fn size(&self) -> usize { + self.transition_type.size() + + self.duration.size() + + self.step.get().size() + + self.fps.size() + + self.angle.size() + + self.pos.size() + + self.bezier.0.size() + + self.bezier.1.size() + + self.wave.size() + + u8::from(self.invert_y).size() + } +} + +impl Deserialize<'_> for Transition { + fn deserialize(buffer: &mut Cursor<&[u8]>) -> Self { + let transition_type = TransitionType::deserialize(buffer); + let duration = f32::deserialize(buffer); + let step = NonZeroU8::new(u8::deserialize(buffer)).unwrap(); + let fps = u16::deserialize(buffer); + let angle = f64::deserialize(buffer); + let pos = Vec2::::deserialize(buffer); + let bezier = ( + Vec2::::deserialize(buffer), + Vec2::::deserialize(buffer), + ); + let wave = Vec2::::deserialize(buffer); + let invert_y = match u8::deserialize(buffer) { + 0 => false, + 1 => true, + _ => unreachable!("`bool` has only two valid values"), + }; + Self { + transition_type, + duration, + step, + fps, + angle, + pos, + bezier, + wave, + invert_y, + } + } +} + +impl Serialize for BitPack<'_> { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + buffer.write_tagged(self.bytes); + self.expected_size.serialize(buffer); + } + + fn size(&self) -> usize { + self.bytes.size_tagged() + self.expected_size.size() + } +} + +impl<'a> Deserialize<'a> for BitPack<'a> { + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { + let bytes = buffer.read_tagged(); + let expected_size = u32::deserialize(buffer); + Self { + bytes, + expected_size, + } + } +} + +impl Serialize for Duration { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + self.as_secs_f64().serialize(buffer); + } + + fn size(&self) -> usize { + size_of::() + } +} + +impl Deserialize<'_> for Duration { + fn deserialize(buffer: &mut Cursor<&[u8]>) -> Self { + Duration::from_secs_f64(f64::deserialize(buffer)) + } +} + +impl Serialize for Animation<'_> { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + self.animation.tag().serialize(buffer); + for frame in self.animation.iter() { + frame.0.serialize(buffer); + frame.1.serialize(buffer); + } + } + + fn size(&self) -> usize { + self.animation.tag().size() + + self + .animation + .iter() + .map(|frame| frame.0.size() + frame.1.size()) + .sum::() + } +} + +impl<'a> Deserialize<'a> for Animation<'a> { + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { + let animation = (0..u32::deserialize(buffer)) + .map(|_| (BitPack::deserialize(buffer), Duration::deserialize(buffer))) + .collect(); + Self { animation } + } +} + +impl Serialize for ImageRequest<'_> { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + self.transition.serialize(buffer); + self.imgs.tag().serialize(buffer); + for image in self.imgs.iter() { + image.serialize(buffer); + } + self.outputs.tag().serialize(buffer); + for &output in self.outputs.iter() { + output.serialize(buffer); + } + let animations = self.animations.as_deref().unwrap_or_default(); + animations.tag().serialize(buffer); + for animation in animations { + animation.serialize(buffer); + } + } + + fn size(&self) -> usize { + let animations = self.animations.as_deref().unwrap_or_default(); + self.transition.size() + + self.imgs.tag().size() + + self.imgs.iter().map(Image::size).sum::() + + self.outputs.tag().size() + + self.outputs.iter().copied().map(str::size).sum::() + + animations.tag().size() + + animations.iter().map(Animation::size).sum::() + } +} + +impl<'a> Deserialize<'a> for ImageRequest<'a> { + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { + let transition = Transition::deserialize(buffer); + let imgs = (0..u32::deserialize(buffer)) + .map(|_| Image::deserialize(buffer)) + .collect(); + let outputs = (0..u32::deserialize(buffer)) + .map(|_| buffer.read_tagged()) + .map(std::str::from_utf8) + .map(|res| res.expect("serializer can write utf8 only")) + .collect(); + let animations = match u32::deserialize(buffer) { + 0 => None, + num => (0..num) + .map(|_| Animation::deserialize(buffer)) + .collect::>() + .into(), + }; + Self { + transition, + imgs, + outputs, + animations, + } + } +} + +impl Serialize for ClearRequest<'_> { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + self.color.as_slice().serialize(buffer); + self.outputs.tag().serialize(buffer); + for &output in self.outputs.iter() { + output.serialize(buffer); + } + } + + fn size(&self) -> usize { + self.color.size() + + self.outputs.tag().size() + + self + .outputs + .iter() + .copied() + .map(Serialize::size_tagged) + .sum::() + } +} + +impl<'a> Deserialize<'a> for ClearRequest<'a> { + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { + let color = buffer + .read(3) + .try_into() + .expect("`[u8; 3]` can be created from three byte slice"); + let outputs = (0..u32::deserialize(buffer)) + .map(|_| buffer.read_tagged()) + .map(std::str::from_utf8) + .map(|res| res.expect("serializer can write utf8 only")) + .collect(); + Self { color, outputs } + } +} + +impl Serialize for Scale { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + match self { + Self::Whole(whole) => { + 0u8.serialize(buffer); + whole.get().serialize(buffer); + } + Self::Fractional(fraction) => { + 1u8.serialize(buffer); + fraction.get().serialize(buffer); + } + } + } + + fn size(&self) -> usize { + size_of::() + size_of::() + } +} + +impl<'a> Deserialize<'a> for Scale { + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { + let tag = u8::deserialize(buffer); + let value = NonZeroI32::new(i32::deserialize(buffer)).unwrap(); + match tag { + 0 => Self::Whole(value), + 1 => Self::Fractional(value), + _ => unreachable!("invalid discriminant"), + } + } +} + +impl Serialize for ImageDescription { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + match self { + ImageDescription::Color(arr) => { + 0u8.serialize(buffer); + arr.as_slice().serialize(buffer); + } + ImageDescription::Img(text) => { + 1u8.serialize(buffer); + text.as_str().serialize(buffer); + } + } + } + + fn size(&self) -> usize { + size_of::() + + match self { + ImageDescription::Color(arr) => size_of_val(arr), + ImageDescription::Img(text) => text.as_str().size_tagged(), + } + } +} + +impl<'a> Deserialize<'a> for ImageDescription { + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { + match u8::deserialize(buffer) { + 0 => Self::Color( + buffer + .read(3) + .try_into() + .expect("`[u8; 3]` can be created from three byte slice"), + ), + 1 => Self::Img( + std::str::from_utf8(buffer.read_tagged()) + .expect("valid utf8") + .into(), + ), + _ => unreachable!("invalid discriminant"), + } + } +} + +impl Serialize for Info { + fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { + buffer.write_tagged(self.name.as_bytes()); + self.dim.serialize(buffer); + self.scale.serialize(buffer); + self.img.serialize(buffer); + self.format.serialize(buffer); + } + + fn size(&self) -> usize { + self.name.as_str().size_tagged() + + self.dim.size() + + self.scale.size() + + self.img.size() + + self.format.size() + } +} + +impl<'a> Deserialize<'a> for Info { + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { + let name = std::str::from_utf8(buffer.read_tagged()) + .expect("valid utf8") + .into(); + let dim = Vec2::::deserialize(buffer); + let scale = Scale::deserialize(buffer); + let img = ImageDescription::deserialize(buffer); + let format = PixelFormat::deserialize(buffer); + Self { + name, + dim, + scale, + img, + format, + } + } +} diff --git a/common/src/ipc/transmit.rs b/common/src/ipc/transmit.rs index 47ecb8a..c248a1b 100644 --- a/common/src/ipc/transmit.rs +++ b/common/src/ipc/transmit.rs @@ -6,10 +6,18 @@ use rustix::io::Errno; use rustix::net; use rustix::net::RecvFlags; +use super::serde::Cursor; +use super::serde::Deserialize; +use super::serde::Serialize; +use super::types2::ClearRequest; +use super::types2::Info; +use super::types2::Request; +use super::types2::Response; use super::Animation; use super::Answer; use super::BgInfo; use super::ClearReq; +use super::Client; use super::ErrnoExt; use super::ImageReq; use super::ImgReq; @@ -18,17 +26,19 @@ use super::IpcErrorKind; use super::IpcSocket; use super::RequestRecv; use super::RequestSend; +use super::Server; use super::Transition; +use crate::ipc::types2::ImageRequest; use crate::mmap::Mmap; use crate::mmap::MmappedStr; // could be enum -pub struct RawMsg { +pub struct IpcMessage { code: Code, shm: Option, } -impl From for RawMsg { +impl From for IpcMessage { fn from(value: RequestSend) -> Self { let code = match value { RequestSend::Ping => Code::ReqPing, @@ -47,7 +57,7 @@ impl From for RawMsg { } } -impl From for RawMsg { +impl From for IpcMessage { fn from(value: Answer) -> Self { let code = match value { Answer::Ok => Code::ResOk, @@ -81,8 +91,8 @@ impl From for RawMsg { } // TODO: remove this ugly mess -impl From for RequestRecv { - fn from(value: RawMsg) -> Self { +impl From for RequestRecv { + fn from(value: IpcMessage) -> Self { match value.code { Code::ReqPing => Self::Ping, Code::ReqQuery => Self::Query, @@ -154,8 +164,8 @@ impl From for RequestRecv { } } -impl From for Answer { - fn from(value: RawMsg) -> Self { +impl From for Answer { + fn from(value: IpcMessage) -> Self { match value.code { Code::ResOk => Self::Ok, Code::ResConfigured => Self::Ping(true), @@ -181,9 +191,10 @@ impl From for Answer { } // TODO: end remove ugly mess block -macro_rules! code { +macro_rules! msg { ($($name:ident $num:literal),* $(,)?) => { - pub enum Code { + #[derive(Copy, Clone, PartialEq, Eq)] + enum Code { $($name,)* } @@ -201,11 +212,10 @@ macro_rules! code { } } } - }; } -code! { +msg! { ReqPing 0, ReqQuery 1, ReqClear 2, @@ -227,22 +237,28 @@ impl TryFrom for Code { // TODO: this along with `RawMsg` should be implementation detail impl IpcSocket { - pub fn send(&self, msg: RawMsg) -> io::Result { + pub fn send(&self, msg: IpcMessage) -> io::Result { let mut payload = [0u8; 16]; - payload[0..8].copy_from_slice(&msg.code.into().to_ne_bytes()); + let mut buf = Cursor::new(payload.as_mut_slice()); + msg.code.into().serialize(&mut buf); - let mut ancillary_buf = [0u8; rustix::cmsg_space!(ScmRights(1))]; - let mut ancillary = net::SendAncillaryBuffer::new(&mut ancillary_buf); + let mut ancillary = [0u8; rustix::cmsg_space!(ScmRights(1))]; + let mut ancillary = net::SendAncillaryBuffer::new(&mut ancillary); let fd; if let Some(ref mmap) = msg.shm { - payload[8..].copy_from_slice(&(mmap.len() as u64).to_ne_bytes()); + debug_assert!( + matches!(msg.code, Code::ReqClear | Code::ReqImg | Code::ResInfo), + "`Mmap` received but not requested" + ); + (mmap.len() as u64).serialize(&mut buf); fd = [mmap.fd()]; let msg = net::SendAncillaryMessage::ScmRights(&fd); ancillary.push(msg); } - let iov = io::IoSlice::new(&payload[..]); + let payload = buf.finish(); + let iov = io::IoSlice::new(payload); net::sendmsg( self.as_fd(), &[iov], @@ -252,11 +268,11 @@ impl IpcSocket { .map(|written| written == payload.len()) } - pub fn recv(&self) -> Result { + pub fn recv(&self) -> Result { let mut buf = [0u8; 16]; - let mut ancillary_buf = [0u8; rustix::cmsg_space!(ScmRights(1))]; + let mut ancillary = [0u8; rustix::cmsg_space!(ScmRights(1))]; - let mut control = net::RecvAncillaryBuffer::new(&mut ancillary_buf); + let mut control = net::RecvAncillaryBuffer::new(&mut ancillary); for _ in 0..5 { let iov = io::IoSliceMut::new(&mut buf); @@ -267,8 +283,9 @@ impl IpcSocket { } } - let code = u64::from_ne_bytes(buf[0..8].try_into().unwrap()).try_into()?; - let len = u64::from_ne_bytes(buf[8..16].try_into().unwrap()) as usize; + let mut buf = Cursor::new(buf.as_slice()); + let code = u64::deserialize(&mut buf).try_into()?; + let len = u64::deserialize(&mut buf) as usize; let shm = if len == 0 { debug_assert!(matches!( @@ -288,6 +305,131 @@ impl IpcSocket { .context(IpcErrorKind::MalformedMsg)?; Some(Mmap::from_fd(file, len)) }; - Ok(RawMsg { code, shm }) + Ok(IpcMessage { code, shm }) + } +} + +impl From> for IpcMessage { + fn from(value: Request) -> Self { + match value { + Request::Ping => Self { + code: Code::ReqPing, + shm: None, + }, + Request::Query => Self { + code: Code::ReqQuery, + shm: None, + }, + Request::Kill => Self { + code: Code::ReqKill, + shm: None, + }, + Request::Clear(clear) => { + let mut mmap = Mmap::create(clear.size()); + clear.serialize(&mut Cursor::new(mmap.slice_mut())); + Self { + code: Code::ReqClear, + shm: Some(mmap), + } + } + Request::Img(image) => { + let mut mmap = Mmap::create(image.size()); + image.serialize(&mut Cursor::new(mmap.slice_mut())); + Self { + code: Code::ReqImg, + shm: Some(mmap), + } + } + } + } +} + +impl<'a> From<&'a IpcMessage> for Request<'a> { + fn from(value: &'a IpcMessage) -> Self { + match value.code { + Code::ReqPing => Self::Ping, + Code::ReqQuery => Self::Query, + Code::ReqKill => Self::Kill, + Code::ReqClear => { + let mmap = value.shm.as_ref().expect("clear request must contain data"); + let clear = ClearRequest::deserialize(&mut Cursor::new(mmap.slice())); + Self::Clear(clear) + } + Code::ReqImg => { + let mmap = value.shm.as_ref().expect("image request must contain data"); + let image = ImageRequest::deserialize(&mut Cursor::new(mmap.slice())); + Self::Img(image) + } + _ => unreachable!("`Request` builder reached invalid state"), + } + } +} + +impl From for IpcMessage { + fn from(value: Response) -> Self { + match value { + Response::Ok => Self { + code: Code::ResOk, + shm: None, + }, + Response::Ping(false) => Self { + code: Code::ResAwait, + shm: None, + }, + Response::Ping(true) => Self { + code: Code::ResConfigured, + shm: None, + }, + Response::Info(info) => { + let mut mmap = Mmap::create(info.size()); + info.serialize(&mut Cursor::new(mmap.slice_mut())); + Self { + code: Code::ReqImg, + shm: Some(mmap), + } + } + } + } +} + +impl From for Response { + fn from(value: IpcMessage) -> Self { + match value.code { + Code::ResOk => Self::Ok, + Code::ResAwait => Self::Ping(false), + Code::ResConfigured => Self::Ping(true), + Code::ResInfo => { + let mmap = value.shm.as_ref().expect("info request must contain data"); + let info = Box::<[Info]>::deserialize(&mut Cursor::new(mmap.slice())); + Self::Info(info) + } + _ => unreachable!("`Response` builder reached invalid state"), + } + } +} + +impl IpcSocket { + /// Send blocking request to `Daemon`, awaiting response + pub fn request(&self, request: Request) -> Result { + self.send(IpcMessage::from(request)) + .context(IpcErrorKind::MalformedMsg)?; + self.recv().map(Response::from) + } +} + +impl IpcSocket { + /// Handle incoming request with `handler` + pub fn handle(&self, handler: impl FnOnce(Request) -> Response) -> Result<(), IpcError> { + let socket = match net::accept(self.as_fd()) { + Ok(stream) => Self::new(stream), + Err(Errno::INTR | Errno::WOULDBLOCK) => return Ok(()), + Err(err) => return Err(err).context(IpcErrorKind::Read), + }; + let request = socket.recv()?; + let response = handler(Request::from(&request)); + socket + .send(IpcMessage::from(response)) + .map(|_| ()) + .context(IpcErrorKind::Bind) } } From 927dfdcf78ff8e4e3478069604ed15771385b360 Mon Sep 17 00:00:00 2001 From: rkuklik Date: Thu, 27 Jun 2024 15:48:31 +0200 Subject: [PATCH 4/4] chore(serde): reorder impl and remove legacy lifetime --- common/src/ipc/serde.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/common/src/ipc/serde.rs b/common/src/ipc/serde.rs index 46f5bfc..ef94955 100644 --- a/common/src/ipc/serde.rs +++ b/common/src/ipc/serde.rs @@ -62,17 +62,17 @@ impl<'a> Cursor<&'a [u8]> { } } -trait Tagged<'a> { +trait Tagged { fn tag(&self) -> u32; } -impl Tagged<'_> for [T] { +impl Tagged for [T] { fn tag(&self) -> u32 { self.len() as u32 } } -impl Tagged<'_> for str { +impl Tagged for str { fn tag(&self) -> u32 { self.as_bytes().tag() } @@ -175,6 +175,14 @@ impl Serialize for Vec2 { } } +impl<'a, T: Deserialize<'a>> Deserialize<'a> for Vec2 { + fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { + let x = T::deserialize(buffer); + let y = T::deserialize(buffer); + Self { x, y } + } +} + impl Serialize for Box<[T]> { fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { self.tag().serialize(buffer); @@ -196,14 +204,6 @@ impl<'a, T: Deserialize<'a>> Deserialize<'a> for Box<[T]> { } } -impl<'a, T: Deserialize<'a>> Deserialize<'a> for Vec2 { - fn deserialize(buffer: &mut Cursor<&'a [u8]>) -> Self { - let x = T::deserialize(buffer); - let y = T::deserialize(buffer); - Self { x, y } - } -} - impl Serialize for PixelFormat { fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) { (*self as u8).serialize(buffer)