llam 0.1.3

Safe, Go-style Rust bindings for the LLAM runtime
use crate::error::{Error, Result};
use crate::sys;
use std::io;
use std::os::raw::{c_short, c_void};

pub type Fd = sys::llam_fd_t;

pub const READABLE: i16 = 0x0001;
pub const WRITABLE: i16 = 0x0004;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PollResult {
    pub ready_count: i32,
    pub revents: i16,
}

pub fn read(fd: Fd, buf: &mut [u8]) -> io::Result<usize> {
    let n = unsafe { sys::llam_read(fd, buf.as_mut_ptr() as *mut c_void, buf.len()) };
    if n < 0 {
        Err(Error::last().into())
    } else {
        Ok(n as usize)
    }
}

pub fn read_when_ready(fd: Fd, buf: &mut [u8], timeout_ms: i32) -> io::Result<usize> {
    let n = unsafe {
        sys::llam_read_when_ready(fd, buf.as_mut_ptr() as *mut c_void, buf.len(), timeout_ms)
    };
    if n < 0 {
        Err(Error::last().into())
    } else {
        Ok(n as usize)
    }
}

pub fn write(fd: Fd, buf: &[u8]) -> io::Result<usize> {
    let n = unsafe { sys::llam_write(fd, buf.as_ptr() as *const c_void, buf.len()) };
    if n < 0 {
        Err(Error::last().into())
    } else {
        Ok(n as usize)
    }
}

pub fn poll_fd(fd: Fd, events: i16, timeout_ms: i32) -> Result<i16> {
    poll(fd, events, timeout_ms).map(|result| result.revents)
}

pub fn poll(fd: Fd, events: i16, timeout_ms: i32) -> Result<PollResult> {
    let mut revents: c_short = 0;
    let rc = unsafe { sys::llam_poll_fd(fd, events as c_short, timeout_ms, &mut revents) };
    if rc >= 0 {
        Ok(PollResult {
            ready_count: rc,
            revents: revents as i16,
        })
    } else {
        Err(Error::last())
    }
}

pub fn accept(fd: Fd) -> io::Result<Fd> {
    let accepted = unsafe { sys::llam_accept(fd, std::ptr::null_mut(), std::ptr::null_mut()) };
    if sys::fd_is_invalid(accepted) {
        Err(Error::last().into())
    } else {
        Ok(accepted)
    }
}

/// Connect a raw socket using LLAM's managed I/O path.
///
/// # Safety
///
/// `addr` and `addrlen` must describe a valid socket address for `fd`.
pub unsafe fn connect_raw(
    fd: Fd,
    addr: *const libc::sockaddr,
    addrlen: sys::socklen_t,
) -> io::Result<()> {
    let rc = sys::llam_connect(fd, addr, addrlen);
    if rc == 0 {
        Ok(())
    } else {
        Err(Error::last().into())
    }
}

pub struct OwnedBuf {
    raw: *mut sys::llam_io_buffer_t,
}

unsafe impl Send for OwnedBuf {}

impl OwnedBuf {
    pub fn as_slice(&self) -> &[u8] {
        if self.raw.is_null() {
            return &[];
        }
        unsafe {
            std::slice::from_raw_parts(
                sys::llam_io_buffer_data(self.raw) as *const u8,
                sys::llam_io_buffer_size(self.raw),
            )
        }
    }

    pub fn capacity(&self) -> usize {
        if self.raw.is_null() {
            0
        } else {
            unsafe { sys::llam_io_buffer_capacity(self.raw) }
        }
    }
}

impl Drop for OwnedBuf {
    fn drop(&mut self) {
        if !self.raw.is_null() {
            unsafe { sys::llam_io_buffer_release(self.raw) };
            self.raw = std::ptr::null_mut();
        }
    }
}

pub fn read_owned(fd: Fd, max_count: usize) -> io::Result<Option<OwnedBuf>> {
    let mut raw = std::ptr::null_mut();
    let n = unsafe { sys::llam_read_owned(fd, max_count, &mut raw) };
    if n < 0 {
        Err(Error::last().into())
    } else if raw.is_null() {
        Ok(None)
    } else {
        Ok(Some(OwnedBuf { raw }))
    }
}

pub fn recv_owned(fd: Fd, max_count: usize, flags: i32) -> io::Result<Option<OwnedBuf>> {
    let mut raw = std::ptr::null_mut();
    let n = unsafe { sys::llam_recv_owned(fd, max_count, flags, &mut raw) };
    if n < 0 {
        Err(Error::last().into())
    } else if raw.is_null() {
        Ok(None)
    } else {
        Ok(Some(OwnedBuf { raw }))
    }
}