use serde::Serialize;
use std::io::{Seek, Write};
#[cfg(unix)]
use std::os::fd::OwnedFd;
#[cfg(feature = "gvariant")]
use crate::gvariant::Serializer as GVSerializer;
use crate::{
container_depths::ContainerDepths,
dbus::Serializer as DBusSerializer,
serialized::{Context, Data, Format, Size, Written},
signature_parser::SignatureParser,
utils::*,
Basic, DynamicType, Error, Result, Signature, WriteBytes,
};
struct NullWriteSeek;
impl Write for NullWriteSeek {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl Seek for NullWriteSeek {
fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
Ok(std::u64::MAX) }
}
pub fn serialized_size<T>(ctxt: Context, value: &T) -> Result<Size>
where
T: ?Sized + Serialize + DynamicType,
{
let mut null = NullWriteSeek;
let signature = value.dynamic_signature();
#[cfg(unix)]
let mut fds = FdList::Number(0);
let len = match ctxt.format() {
Format::DBus => {
let mut ser = DBusSerializer::<NullWriteSeek>::new(
signature,
&mut null,
#[cfg(unix)]
&mut fds,
ctxt,
)?;
value.serialize(&mut ser)?;
ser.0.bytes_written
}
#[cfg(feature = "gvariant")]
Format::GVariant => {
let mut ser = GVSerializer::<NullWriteSeek>::new(
signature,
&mut null,
#[cfg(unix)]
&mut fds,
ctxt,
)?;
value.serialize(&mut ser)?;
ser.0.bytes_written
}
};
let size = Size::new(len, ctxt);
#[cfg(unix)]
let size = match fds {
FdList::Number(n) => size.set_num_fds(n),
FdList::Fds(_) => unreachable!("`Fds::Fds` is not possible here"),
};
Ok(size)
}
pub unsafe fn to_writer<W, T>(writer: &mut W, ctxt: Context, value: &T) -> Result<Written>
where
W: Write + Seek,
T: ?Sized + Serialize + DynamicType,
{
let signature = value.dynamic_signature();
to_writer_for_signature(writer, ctxt, &signature, value)
}
pub fn to_bytes<T>(ctxt: Context, value: &T) -> Result<Data<'static, 'static>>
where
T: ?Sized + Serialize + DynamicType,
{
to_bytes_for_signature(ctxt, value.dynamic_signature(), value)
}
pub unsafe fn to_writer_for_signature<'s, W, S, T>(
writer: &mut W,
ctxt: Context,
signature: S,
value: &T,
) -> Result<Written>
where
W: Write + Seek,
S: TryInto<Signature<'s>>,
S::Error: Into<Error>,
T: ?Sized + Serialize,
{
#[cfg(unix)]
let mut fds = FdList::Fds(vec![]);
let len = match ctxt.format() {
Format::DBus => {
let mut ser = DBusSerializer::<W>::new(
signature,
writer,
#[cfg(unix)]
&mut fds,
ctxt,
)?;
value.serialize(&mut ser)?;
ser.0.bytes_written
}
#[cfg(feature = "gvariant")]
Format::GVariant => {
let mut ser = GVSerializer::<W>::new(
signature,
writer,
#[cfg(unix)]
&mut fds,
ctxt,
)?;
value.serialize(&mut ser)?;
ser.0.bytes_written
}
};
let written = Written::new(len, ctxt);
#[cfg(unix)]
let written = match fds {
FdList::Fds(fds) => written.set_fds(fds),
FdList::Number(_) => unreachable!("`Fds::Number` is not possible here"),
};
Ok(written)
}
pub fn to_bytes_for_signature<'s, S, T>(
ctxt: Context,
signature: S,
value: &T,
) -> Result<Data<'static, 'static>>
where
S: TryInto<Signature<'s>>,
S::Error: Into<Error>,
T: ?Sized + Serialize,
{
let mut cursor = std::io::Cursor::new(vec![]);
let ret = unsafe { to_writer_for_signature(&mut cursor, ctxt, signature, value) }?;
#[cfg(unix)]
let encoded = Data::new_fds(cursor.into_inner(), ctxt, ret.into_fds());
#[cfg(not(unix))]
let encoded = {
let _ = ret;
Data::new(cursor.into_inner(), ctxt)
};
Ok(encoded)
}
pub(crate) struct SerializerCommon<'ser, 'sig, W> {
pub(crate) ctxt: Context,
pub(crate) writer: &'ser mut W,
pub(crate) bytes_written: usize,
#[cfg(unix)]
pub(crate) fds: &'ser mut FdList,
pub(crate) sig_parser: SignatureParser<'sig>,
pub(crate) value_sign: Option<Signature<'static>>,
pub(crate) container_depths: ContainerDepths,
}
#[cfg(unix)]
pub(crate) enum FdList {
Fds(Vec<OwnedFd>),
Number(u32),
}
impl<'ser, 'sig, W> SerializerCommon<'ser, 'sig, W>
where
W: Write + Seek,
{
#[cfg(unix)]
pub(crate) fn add_fd(&mut self, fd: std::os::fd::RawFd) -> Result<u32> {
use std::os::fd::{AsRawFd, BorrowedFd};
match self.fds {
FdList::Fds(fds) => {
if let Some(idx) = fds.iter().position(|x| x.as_raw_fd() == fd) {
return Ok(idx as u32);
}
let idx = fds.len();
let fd = unsafe { BorrowedFd::borrow_raw(fd) }.try_clone_to_owned()?;
fds.push(fd);
Ok(idx as u32)
}
FdList::Number(n) => {
let idx = *n;
*n += 1;
Ok(idx)
}
}
}
pub(crate) fn add_padding(&mut self, alignment: usize) -> Result<usize> {
let padding = padding_for_n_bytes(self.abs_pos(), alignment);
if padding > 0 {
let byte = [0_u8; 1];
for _ in 0..padding {
self.write_all(&byte)
.map_err(|e| Error::InputOutput(e.into()))?;
}
}
Ok(padding)
}
pub(crate) fn prep_serialize_basic<T>(&mut self) -> Result<()>
where
T: Basic,
{
self.sig_parser.skip_char()?;
self.add_padding(T::alignment(self.ctxt.format()))?;
Ok(())
}
pub(crate) fn prep_serialize_enum_variant(&mut self, variant_index: u32) -> Result<()> {
let signature = self.sig_parser.next_signature()?;
if self.sig_parser.next_char()? != STRUCT_SIG_START_CHAR {
return Err(Error::SignatureMismatch(
signature.to_owned(),
format!("expected `{STRUCT_SIG_START_CHAR}`"),
));
}
let alignment = alignment_for_signature(&signature, self.ctxt.format())?;
self.add_padding(alignment)?;
self.write_u32(self.ctxt.endian(), variant_index)
.map_err(|e| Error::InputOutput(e.into()))?;
self.sig_parser.skip_chars(2)?;
Ok(())
}
fn abs_pos(&self) -> usize {
self.ctxt.position() + self.bytes_written
}
}
impl<'ser, 'sig, W> Write for SerializerCommon<'ser, 'sig, W>
where
W: Write + Seek,
{
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.writer.write(buf).map(|n| {
self.bytes_written += n;
n
})
}
fn flush(&mut self) -> std::io::Result<()> {
self.writer.flush()
}
}