use std::borrow::Cow;
use std::ffi::{CStr, CString};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::num::NonZeroU32;
use std::os::fd::OwnedFd;
mod ring_buffer;
pub mod transport;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IoMode {
Blocking,
NonBlocking,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ObjectId(pub NonZeroU32);
impl ObjectId {
pub const DISPLAY: Self = Self(unsafe { NonZeroU32::new_unchecked(1) });
pub const MAX_CLIENT: Self = Self(unsafe { NonZeroU32::new_unchecked(0xFEFFFFFF) });
pub const MIN_SERVER: Self = Self(unsafe { NonZeroU32::new_unchecked(0xFF000000) });
pub fn as_u32(self) -> u32 {
self.0.get()
}
pub fn created_by_server(self) -> bool {
self >= Self::MIN_SERVER
}
pub fn created_by_client(self) -> bool {
self <= Self::MAX_CLIENT
}
}
#[derive(Debug, Clone, Copy)]
pub struct MessageHeader {
pub object_id: ObjectId,
pub size: u16,
pub opcode: u16,
}
impl MessageHeader {
pub const SIZE: usize = 8;
}
#[derive(Debug)]
pub struct Message {
pub header: MessageHeader,
pub args: Vec<ArgValue>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum ArgType {
Int,
Uint,
Fixed,
Object,
OptObject,
NewId(&'static Interface),
AnyNewId,
String,
OptString,
Array,
Fd,
}
#[derive(Debug)]
pub enum ArgValue {
Int(i32),
Uint(u32),
Fixed(Fixed),
Object(ObjectId),
OptObject(Option<ObjectId>),
NewId(ObjectId),
AnyNewId(Cow<'static, CStr>, u32, ObjectId),
String(CString),
OptString(Option<CString>),
Array(Vec<u8>),
Fd(OwnedFd),
}
impl ArgValue {
pub fn size(&self) -> usize {
fn len_with_padding(len: usize) -> usize {
let padding = (4 - (len % 4)) % 4;
4 + len + padding
}
match self {
Self::Int(_)
| Self::Uint(_)
| Self::Fixed(_)
| Self::Object(_)
| Self::OptObject(_)
| Self::NewId(_)
| Self::OptString(None) => 4,
Self::AnyNewId(iface, _version, _id) => {
len_with_padding(iface.to_bytes_with_nul().len()) + 8
}
Self::String(string) | Self::OptString(Some(string)) => {
len_with_padding(string.to_bytes_with_nul().len())
}
Self::Array(array) => len_with_padding(array.len()),
Self::Fd(_) => 0,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Fixed(pub i32);
impl From<i32> for Fixed {
fn from(value: i32) -> Self {
Self(value * 256)
}
}
impl From<u32> for Fixed {
fn from(value: u32) -> Self {
Self(value as i32 * 256)
}
}
impl From<f32> for Fixed {
fn from(value: f32) -> Self {
Self((value * 256.0) as i32)
}
}
impl From<f64> for Fixed {
fn from(value: f64) -> Self {
Self((value * 256.0) as i32)
}
}
impl Fixed {
pub const ZERO: Self = Self(0);
pub const ONE: Self = Self(256);
pub const MINUS_ONE: Self = Self(-256);
pub fn as_f64(self) -> f64 {
self.0 as f64 / 256.0
}
pub fn as_f32(self) -> f32 {
self.0 as f32 / 256.0
}
pub fn as_int(self) -> i32 {
self.0 / 256
}
pub fn is_int(self) -> bool {
self.0 & 255 == 0
}
}
impl fmt::Debug for Fixed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_f64().fmt(f)
}
}
pub struct Interface {
pub name: &'static CStr,
pub version: u32,
pub events: &'static [MessageDesc],
pub requests: &'static [MessageDesc],
}
#[derive(Debug, Clone, Copy)]
pub struct MessageDesc {
pub name: &'static str,
pub is_destructor: bool,
pub signature: &'static [ArgType],
}
impl PartialEq for &'static Interface {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl Eq for &'static Interface {}
impl Hash for &'static Interface {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl fmt::Debug for Interface {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Interface").field(&self.name).finish()
}
}
#[derive(Default)]
pub struct MessageBuffersPool {
pool: Vec<Vec<ArgValue>>,
}
impl MessageBuffersPool {
pub fn reuse_args(&mut self, mut buf: Vec<ArgValue>) {
buf.clear();
self.pool.push(buf);
}
pub fn get_args(&mut self) -> Vec<ArgValue> {
self.pool.pop().unwrap_or_default()
}
}