use core::{fmt, marker::PhantomData, mem::ManuallyDrop};
use crate::{
io,
os::{
fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd},
AsInner, FromInner, IntoInner,
},
sys,
sys::io::{sceIoClose, sceIoLseek, sceIoRead, sceIoWrite, FileId, Whence},
};
#[derive(Copy, Clone)]
#[repr(transparent)]
#[rustc_nonnull_optimization_guaranteed]
pub struct BorrowedFd<'fd> {
fd: FileId,
_phantom: PhantomData<&'fd OwnedFd>,
}
#[repr(transparent)]
#[rustc_nonnull_optimization_guaranteed]
pub struct OwnedFd {
fd: FileId,
}
impl BorrowedFd<'_> {
#[inline]
#[track_caller]
pub const unsafe fn borrow_raw(fd: RawFd) -> Self {
Self {
fd: FileId::from_raw(fd).expect("fd not in valid range"),
_phantom: PhantomData,
}
}
pub fn try_clone_to_owned(&self) -> io::Result<OwnedFd> {
Err(io_core::io_const_error!(
io::ErrorKind::Unsupported,
"operation not supported on this platform"
))
}
}
impl BorrowedFd<'_> {
#[inline]
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
if sys::is_interrupt_enabled() {
let res = unsafe { sceIoRead(self.fd, buf.as_mut_ptr().cast(), buf.len()) };
res.into_result().map_err(Into::into)
} else {
Err(io::Error::from(sys::SceError::IO))
}
}
#[inline]
pub fn read_buf(&self, mut cursor: io::BorrowedCursor<'_>) -> io::Result<()> {
if sys::is_interrupt_enabled() {
let res = unsafe {
sceIoRead(self.fd, cursor.as_mut().as_mut_ptr().cast(), cursor.capacity())
};
let ret = res.into_result()?;
unsafe {
cursor.advance(ret);
}
Ok(())
} else {
Err(io::Error::from(sys::SceError::IO))
}
}
#[inline]
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
if sys::is_interrupt_enabled() {
unsafe {
sceIoWrite(self.fd, buf.as_ptr().cast(), buf.len())
.into_result()
.map_err(Into::into)
}
} else {
Err(io::Error::from(sys::SceError::IO))
}
}
#[inline]
pub fn write_all(&self, mut buf: &[u8]) -> io::Result<()> {
while !buf.is_empty() {
match self.write(buf) {
Ok(0) => {
return Err(io_core::io_const_error!(
io::ErrorKind::WriteZero,
"failed to write whole buffer"
));
},
Ok(n) => buf = &buf[n..],
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {},
Err(e) => return Err(e),
}
}
Ok(())
}
#[inline]
pub fn seek(&self, pos: io::SeekFrom) -> io::Result<u64> {
if sys::is_interrupt_enabled() {
let (whence, pos) = match pos {
io::SeekFrom::Start(off) => (Whence::Start, off as i64),
io::SeekFrom::End(off) => (Whence::End, off),
io::SeekFrom::Current(off) => (Whence::Current, off),
};
sceIoLseek(self.fd, pos, whence).into_result().map_err(From::from)
} else {
Err(io::Error::from(sys::SceError::IO))
}
}
#[inline]
#[doc(alias = "tell")]
pub fn stream_position(&self) -> io::Result<u64> {
self.seek(io::SeekFrom::Current(0))
}
}
impl OwnedFd {
pub(crate) fn as_file_id(&self) -> FileId {
self.fd
}
pub fn try_clone(&self) -> io::Result<Self> {
self.as_fd().try_clone_to_owned()
}
}
impl AsRawFd for BorrowedFd<'_> {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.fd.to_inner()
}
}
impl AsRawFd for OwnedFd {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.fd.to_inner()
}
}
impl IntoRawFd for OwnedFd {
#[inline]
fn into_raw_fd(self) -> RawFd {
ManuallyDrop::new(self).fd.to_inner()
}
}
impl AsInner<FileId> for OwnedFd {
#[inline]
fn as_inner(&self) -> &FileId {
&self.fd
}
}
impl AsInner<FileId> for BorrowedFd<'_> {
#[inline]
fn as_inner(&self) -> &FileId {
&self.fd
}
}
impl IntoInner<FileId> for OwnedFd {
fn into_inner(self) -> FileId {
self.fd
}
}
impl FromInner<FileId> for OwnedFd {
fn from_inner(inner: FileId) -> Self {
Self { fd: inner }
}
}
impl FromRawFd for OwnedFd {
#[inline]
#[track_caller]
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self {
fd: FileId::from_raw(fd).expect("fd not in valid range"),
}
}
}
impl Drop for OwnedFd {
#[inline]
fn drop(&mut self) {
unsafe {
if sys::is_interrupt_enabled() {
let mut res = sceIoClose(self.fd);
if res.is_ok() {
return;
}
let mut atempts = 0;
while atempts < 0x10 && res.is_err() {
res = sceIoClose(self.fd);
atempts += 1;
}
}
}
}
}
impl fmt::Debug for BorrowedFd<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BorrowedFd").field("fd", &self.fd).finish()
}
}
impl fmt::Debug for OwnedFd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedFd").field("fd", &self.fd).finish()
}
}
impl io::Read for &BorrowedFd<'_> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(**self).read(buf)
}
#[inline]
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
(**self).read_buf(buf)
}
}
impl fmt::Write for &BorrowedFd<'_> {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write_all(s.as_bytes()).map_err(|_| fmt::Error)
}
}
impl io::Write for &BorrowedFd<'_> {
#[inline]
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
(**self).write(data)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl io::Seek for &BorrowedFd<'_> {
#[inline]
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
(**self).seek(pos)
}
#[inline]
fn stream_position(&mut self) -> io::Result<u64> {
(**self).stream_position()
}
}
pub trait AsFd {
fn as_fd(&self) -> BorrowedFd<'_>;
}
impl<T: AsFd + ?Sized> AsFd for &T {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
T::as_fd(self)
}
}
impl<T: AsFd + ?Sized> AsFd for &mut T {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
T::as_fd(self)
}
}
impl AsFd for BorrowedFd<'_> {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
*self
}
}
impl AsFd for OwnedFd {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
}
}
impl<T: AsFd + ?Sized> AsFd for alloc::sync::Arc<T> {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
(**self).as_fd()
}
}
impl<T: AsFd + ?Sized> AsFd for alloc::rc::Rc<T> {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
(**self).as_fd()
}
}
impl<T: AsFd + ?Sized> AsFd for alloc::boxed::Box<T> {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
(**self).as_fd()
}
}