io-impl 0.11.0

I/O implementations
Documentation
#![cfg(target_family = "unix")]
#![cfg(not(tarpaulin_include))]

use std::{ffi::CStr, io, mem::zeroed, thread::yield_now};

use io_trait::OperationResult;
use libc::{
    aio_cancel, aio_read, aio_return, aio_write, aiocb, c_int, close, open, AIO_NOTCANCELED,
};

use crate::async_traits::AsyncTrait;

#[derive(Debug, PartialEq, Eq)]
#[repr(transparent)]
struct AioError(c_int);
const EINPROGRESS: AioError = AioError(libc::EINPROGRESS);

fn aio_error(overlapped: &aiocb) -> AioError {
    AioError(unsafe { libc::aio_error(overlapped) })
}

pub struct Unix();

fn to_result(result: c_int) -> io::Result<c_int> {
    if result == -1 {
        return Err(io::Error::last_os_error());
    }
    Ok(result)
}

fn to_operation_result(result: c_int) -> io::Result<()> {
    to_result(result).map(|_| ())
}

impl AsyncTrait for Unix {
    type Handle = i32;
    type Overlapped = aiocb;
    fn overlapped_default() -> Self::Overlapped {
        unsafe { zeroed() }
    }
    fn close(handle: Self::Handle) {
        unsafe { close(handle) };
    }
    fn cancel(handle: Self::Handle, overlapped: &mut Self::Overlapped) {
        if unsafe { aio_cancel(handle, overlapped) } != AIO_NOTCANCELED {
            return;
        }
        loop {
            yield_now();
            if aio_error(overlapped) != EINPROGRESS {
                return;
            }
        }
    }
    fn get_result(_handle: Self::Handle, overlapped: &mut Self::Overlapped) -> OperationResult {
        match aio_error(overlapped) {
            AioError(0) => OperationResult::Ok(unsafe { aio_return(overlapped) } as usize),
            EINPROGRESS => OperationResult::Pending,
            e => OperationResult::Err(io::Error::from_raw_os_error(e.0)),
        }
    }
    fn open(path: &CStr, create: bool) -> io::Result<Self::Handle> {
        let oflag = if create {
            libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC
        } else {
            libc::O_RDONLY
        };
        to_result(unsafe { open(path.as_ptr(), oflag, 0o644) })
    }
    fn init_overlapped(
        handle: Self::Handle,
        overlapped: &mut Self::Overlapped,
        offset: u64,
        buffer: &[u8],
    ) {
        *overlapped = unsafe { zeroed() };
        overlapped.aio_fildes = handle;
        overlapped.aio_buf = buffer.as_ptr() as *mut _;
        overlapped.aio_nbytes = buffer.len();
        overlapped.aio_offset = offset as i64;
    }
    fn read(
        _handle: Self::Handle,
        overlapped: &mut Self::Overlapped,
        _buffer: &mut [u8],
    ) -> io::Result<()> {
        to_operation_result(unsafe { aio_read(overlapped) })
    }

    fn write(
        _handle: Self::Handle,
        overlapped: &mut Self::Overlapped,
        _buffer: &[u8],
    ) -> io::Result<()> {
        to_operation_result(unsafe { aio_write(overlapped) })
    }
}

pub type Os = Unix;