#![allow(clippy::bad_bit_mask)] use crate::{object_get_info, object_get_property, object_set_property};
use crate::{ok, Status};
use crate::{AsHandleRef, Handle, HandleBased, HandleRef, Peered};
use crate::{ObjectQuery, Topic};
use crate::{Property, PropertyQuery};
use bitflags::bitflags;
use fuchsia_zircon_sys as sys;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Socket(Handle);
impl_handle_based!(Socket);
impl Peered for Socket {}
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SocketOpts: u32 {
const STREAM = 0 << 0;
const DATAGRAM = 1 << 0;
}
}
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SocketReadOpts: u32 {
const PEEK = 1 << 3;
}
}
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SocketWriteOpts: u32 {
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SocketWriteDisposition {
Enabled,
Disabled,
}
impl SocketWriteDisposition {
pub fn from_raw(raw: u32) -> Result<Option<SocketWriteDisposition>, Status> {
match raw {
0 => Ok(None),
sys::ZX_SOCKET_DISPOSITION_WRITE_ENABLED => Ok(Some(SocketWriteDisposition::Enabled)),
sys::ZX_SOCKET_DISPOSITION_WRITE_DISABLED => Ok(Some(SocketWriteDisposition::Disabled)),
_ => Err(Status::INVALID_ARGS),
}
}
}
impl From<SocketWriteDisposition> for u32 {
fn from(disposition: SocketWriteDisposition) -> Self {
match disposition {
SocketWriteDisposition::Enabled => sys::ZX_SOCKET_DISPOSITION_WRITE_ENABLED,
SocketWriteDisposition::Disabled => sys::ZX_SOCKET_DISPOSITION_WRITE_DISABLED,
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct SocketInfo {
pub options: SocketOpts,
pub rx_buf_max: usize,
pub rx_buf_size: usize,
pub rx_buf_available: usize,
pub tx_buf_max: usize,
pub tx_buf_size: usize,
}
impl Default for SocketInfo {
fn default() -> SocketInfo {
SocketInfo {
options: SocketOpts::STREAM,
rx_buf_max: 0,
rx_buf_size: 0,
rx_buf_available: 0,
tx_buf_max: 0,
tx_buf_size: 0,
}
}
}
impl From<sys::zx_info_socket_t> for SocketInfo {
fn from(socket: sys::zx_info_socket_t) -> SocketInfo {
SocketInfo {
options: SocketOpts::from_bits_truncate(socket.options),
rx_buf_max: socket.rx_buf_max,
rx_buf_size: socket.rx_buf_size,
rx_buf_available: socket.rx_buf_available,
tx_buf_max: socket.tx_buf_max,
tx_buf_size: socket.tx_buf_size,
}
}
}
struct SocketInfoQuery;
unsafe impl ObjectQuery for SocketInfoQuery {
const TOPIC: Topic = Topic::SOCKET;
type InfoTy = sys::zx_info_socket_t;
}
impl Socket {
pub fn create_stream() -> (Socket, Socket) {
Self::try_create(SocketOpts::STREAM).expect("socket creation can't fail with valid options")
}
pub fn create_datagram() -> (Socket, Socket) {
Self::try_create(SocketOpts::DATAGRAM)
.expect("socket creation can't fail with valid options")
}
fn try_create(sock_opts: SocketOpts) -> Result<(Socket, Socket), Status> {
unsafe {
let mut out0 = 0;
let mut out1 = 0;
let status = sys::zx_socket_create(sock_opts.bits(), &mut out0, &mut out1);
ok(status)?;
Ok((Self::from(Handle::from_raw(out0)), Self::from(Handle::from_raw(out1))))
}
}
pub fn write(&self, bytes: &[u8]) -> Result<usize, Status> {
self.write_opts(bytes, SocketWriteOpts::default())
}
pub fn write_opts(&self, bytes: &[u8], opts: SocketWriteOpts) -> Result<usize, Status> {
let mut actual = 0;
let status = unsafe {
sys::zx_socket_write(
self.raw_handle(),
opts.bits(),
bytes.as_ptr(),
bytes.len(),
&mut actual,
)
};
ok(status).map(|()| actual)
}
pub fn read(&self, bytes: &mut [u8]) -> Result<usize, Status> {
self.read_opts(bytes, SocketReadOpts::default())
}
pub fn read_opts(&self, bytes: &mut [u8], opts: SocketReadOpts) -> Result<usize, Status> {
let mut actual = 0;
let status = unsafe {
sys::zx_socket_read(
self.raw_handle(),
opts.bits(),
bytes.as_mut_ptr(),
bytes.len(),
&mut actual,
)
};
ok(status).map(|()| actual)
}
pub fn half_close(&self) -> Result<(), Status> {
self.set_disposition(None, Some(SocketWriteDisposition::Disabled))
}
pub fn set_disposition(
&self,
disposition: Option<SocketWriteDisposition>,
disposition_peer: Option<SocketWriteDisposition>,
) -> Result<(), Status> {
let status = unsafe {
sys::zx_socket_set_disposition(
self.raw_handle(),
disposition.map(u32::from).unwrap_or(0),
disposition_peer.map(u32::from).unwrap_or(0),
)
};
ok(status)
}
pub fn outstanding_read_bytes(&self) -> Result<usize, Status> {
Ok(self.info()?.rx_buf_available)
}
pub fn info(&self) -> Result<SocketInfo, Status> {
let mut info = sys::zx_info_socket_t::default();
object_get_info::<SocketInfoQuery>(self.as_handle_ref(), std::slice::from_mut(&mut info))
.map(|_| SocketInfo::from(info))
}
}
unsafe_handle_properties!(object: Socket,
props: [
{query_ty: SOCKET_RX_THRESHOLD, tag: SocketRxThresholdTag, prop_ty: usize, get:get_rx_threshold, set: set_rx_threshold},
{query_ty: SOCKET_TX_THRESHOLD, tag: SocketTxThresholdTag, prop_ty: usize, get:get_tx_threshold, set: set_tx_threshold},
]
);
#[cfg(test)]
mod tests {
use super::*;
fn socket_basic_helper(opts: SocketOpts) {
let (s1, s2) = match opts {
SocketOpts::STREAM => Socket::create_stream(),
SocketOpts::DATAGRAM => Socket::create_datagram(),
_ => panic!("unsupported socket options"),
};
assert_eq!(s1.write(b"hello").unwrap(), 5);
assert_eq!(s1.write(b"world").unwrap(), 5);
let mut read_vec = vec![0; 11];
if opts == SocketOpts::DATAGRAM {
assert_eq!(s2.read(&mut read_vec).unwrap(), 5);
assert_eq!(&read_vec[0..5], b"hello");
assert_eq!(s2.read(&mut read_vec).unwrap(), 5);
assert_eq!(&read_vec[0..5], b"world");
} else {
assert_eq!(s2.read(&mut read_vec).unwrap(), 10);
assert_eq!(&read_vec[0..10], b"helloworld");
}
assert_eq!(s2.read(&mut read_vec), Err(Status::SHOULD_WAIT));
assert!(s1.half_close().is_ok());
assert_eq!(s2.write(b"fail"), Err(Status::BAD_STATE));
assert_eq!(s1.read(&mut read_vec), Err(Status::BAD_STATE));
assert_eq!(s2.read(&mut read_vec), Err(Status::SHOULD_WAIT));
assert_eq!(s1.write(b"back").unwrap(), 4);
assert_eq!(s2.read(&mut read_vec).unwrap(), 4);
assert_eq!(&read_vec[0..4], b"back");
}
#[test]
fn socket_basic() {
socket_basic_helper(SocketOpts::STREAM);
socket_basic_helper(SocketOpts::DATAGRAM);
}
#[test]
fn socket_info() {
let (s1, s2) = Socket::create_stream();
let s1info = s1.info().unwrap();
assert_eq!(s1info.rx_buf_available, 0);
assert_eq!(s1info.rx_buf_size, 0);
assert_eq!(s1info.tx_buf_size, 0);
assert_eq!(s1.write(b"hello").unwrap(), 5);
let s1info = s1.info().unwrap();
let s2info = s2.info().unwrap();
assert_eq!(s1info.tx_buf_size, 5);
assert_eq!(s1info.rx_buf_size, 0);
assert_eq!(s2info.rx_buf_size, 5);
assert_eq!(s2info.rx_buf_available, 5);
assert_eq!(s2info.tx_buf_size, 0);
}
#[test]
fn socket_disposition() {
const PAYLOAD: &'static [u8] = b"Hello";
let (s1, s2) = Socket::create_stream();
assert_eq!(
s1.set_disposition(
Some(SocketWriteDisposition::Disabled),
Some(SocketWriteDisposition::Enabled)
),
Ok(())
);
assert_eq!(s2.write(PAYLOAD), Ok(PAYLOAD.len()));
assert_eq!(s1.write(PAYLOAD), Err(Status::BAD_STATE));
let mut buf = [0u8; PAYLOAD.len() + 1];
assert_eq!(s1.read(&mut buf[..]), Ok(PAYLOAD.len()));
assert_eq!(&buf[..PAYLOAD.len()], PAYLOAD);
assert_eq!(s1.set_disposition(None, None), Ok(()));
assert_eq!(s2.write(PAYLOAD), Ok(PAYLOAD.len()));
assert_eq!(s1.write(PAYLOAD), Err(Status::BAD_STATE));
}
}