You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
835 lines
19 KiB
835 lines
19 KiB
//! LOw Level Interface
|
|
//!
|
|
//! Provides interfaces to low-level operations and syscalls.
|
|
use std::ops::{Deref, DerefMut};
|
|
use std::{
|
|
fmt,
|
|
error,
|
|
};
|
|
use bytes::{
|
|
Buf,
|
|
};
|
|
use std::os::unix::io::*;
|
|
use std::ffi::{
|
|
CStr, CString,
|
|
};
|
|
|
|
//TODO: Provide adequate wrappers for the extern functions below
|
|
|
|
/// Raw exports
|
|
mod raw {
|
|
pub(super) use std::os::raw::*;
|
|
use libc::{
|
|
off64_t as c_off64_t,
|
|
off_t as c_off_t,
|
|
pid_t as c_pid_t,
|
|
size_t as c_size_t,
|
|
};
|
|
|
|
|
|
// Function definitions
|
|
|
|
extern "C" {
|
|
pub fn memfd_create(name: *const c_char, flags: c_int) -> c_int;
|
|
|
|
pub fn dup(fd: c_int) -> c_int;
|
|
pub fn dup2(from: c_int, to: c_int) -> c_int;
|
|
|
|
pub fn ftruncate(fd: c_int, length: c_off_t) -> c_int;
|
|
pub fn ftruncate64(fd: c_int, length: c_off64_t) -> c_int;
|
|
|
|
pub fn close(fd: c_int) -> c_int;
|
|
|
|
pub fn getenv(name: *const c_char) -> *mut c_char;
|
|
pub fn getpid() -> c_pid_t;
|
|
|
|
pub fn mmap(
|
|
addr: *mut c_void,
|
|
len: c_size_t,
|
|
prot: c_int,
|
|
flags: c_int,
|
|
fd: c_int,
|
|
offset: c_off_t
|
|
) -> *mut c_void;
|
|
|
|
pub fn mmap64(
|
|
addr: *mut c_void,
|
|
len: c_size_t,
|
|
prot: c_int,
|
|
flags: c_int,
|
|
fd: c_int,
|
|
offset: c_off64_t
|
|
) -> *mut c_void;
|
|
}
|
|
|
|
// Re-exports
|
|
|
|
pub use libc::MAP_FAILED;
|
|
pub use std::ffi::c_void as Opaque;
|
|
pub use libc::c_char as Char;
|
|
}
|
|
|
|
// Errno error handling
|
|
|
|
//TODO: Move all errno related stuff into a submodule inside `srd/loli/` to un-clutter this file; and add a `RawErrno`-like struct for errors that are *just* an errno value (that implements `SystemError`)
|
|
|
|
/// A raw `errno` value
|
|
pub type RawErrno = raw::c_int;
|
|
|
|
/// Default max buffer size for formatting `errno` message codes.
|
|
///
|
|
/// This is the size used by glibc's `perror()`. It should cover all internationalised error messages.
|
|
pub const DEFAULT_ERRNO_MESSAGE_BUFFER_SIZE: usize = 1024;
|
|
|
|
/// Default buffer size short `errno` message code strings.
|
|
///
|
|
/// # Internationalisation
|
|
/// This size covers all current English `errno` messages. However, if the program is ran with a non-ASCII locale, the size may exceed this by far. See `DEFAULT_ERRNO_MESSAGE_BUFFER_SIZE` for those cases.
|
|
pub const DEFAULT_ASCII_ERRNO_MESSAGE_BUFFER_SIZE: usize = 64;
|
|
|
|
/// An error type which also contains an `errno` code.
|
|
//TODO: A way to integrate this into eyre::Reports? Maybe in the extension trait? `.to_report_with_errno()` or something?
|
|
pub trait SystemError: std::error::Error
|
|
{
|
|
fn code(&self) -> RawErrno;
|
|
|
|
#[inline(always)]
|
|
fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
errno_fmt_message::<1024>(self.code(), f)
|
|
}
|
|
|
|
#[inline]
|
|
fn get_message(&self) -> ErrnoFormatter
|
|
{
|
|
ErrnoFormatter::new_default(self.code())
|
|
}
|
|
|
|
#[inline]
|
|
fn message(&self) -> String
|
|
{
|
|
self.get_message().to_string()
|
|
}
|
|
}
|
|
|
|
/// Extension trait for `SystemError` types.
|
|
///
|
|
/// # Specific buffered sizes
|
|
/// Provides user-configurable buffer sized methods for message formatting
|
|
pub trait SystemErrorExt: SystemError
|
|
{
|
|
fn fmt_message_with_buffer<const BUFFER_SIZE: usize>(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
|
|
fn get_message_with_buffer<const BUFFER_SIZE: usize>(&self) -> ErrnoFormatter<BUFFER_SIZE>;
|
|
fn message_with_buffer<const BUFFER_SIZE: usize>(&self) -> String;
|
|
}
|
|
|
|
impl<T: ?Sized> SystemErrorExt for T
|
|
where T: SystemError
|
|
{
|
|
#[inline]
|
|
fn fmt_message_with_buffer<const BUFFER_SIZE: usize>(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
errno_fmt_message::<BUFFER_SIZE>(self.code(), f)
|
|
}
|
|
|
|
#[inline]
|
|
fn get_message_with_buffer<const BUFFER_SIZE: usize>(&self) -> ErrnoFormatter<BUFFER_SIZE>
|
|
{
|
|
ErrnoFormatter::<BUFFER_SIZE>::new(self.code())
|
|
}
|
|
#[inline]
|
|
fn message_with_buffer<const BUFFER_SIZE: usize>(&self) -> String
|
|
{
|
|
ErrnoFormatter::<BUFFER_SIZE>::new(self.code()).to_string()
|
|
}
|
|
}
|
|
|
|
/// String formatter for an `errno` value.
|
|
///
|
|
/// # `MESSAGE_BUFFER_SIZE`
|
|
/// This structure contains only the `errno` code. The `MESSAGE_BUFFER_SIZE` const generic is used as a max size for formatting the error messages.
|
|
/// It **must** be above 0.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
|
|
#[repr(transparent)]
|
|
pub struct ErrnoFormatter<const MESSAGE_BUFFER_SIZE: usize = DEFAULT_ERRNO_MESSAGE_BUFFER_SIZE>(RawErrno);
|
|
|
|
//TODO: Document the following
|
|
|
|
impl ErrnoFormatter<DEFAULT_ASCII_ERRNO_MESSAGE_BUFFER_SIZE>
|
|
{
|
|
#[inline(always)]
|
|
pub const fn new_ascii(code: RawErrno) -> Self
|
|
{
|
|
Self(code)
|
|
}
|
|
}
|
|
|
|
impl ErrnoFormatter<DEFAULT_ERRNO_MESSAGE_BUFFER_SIZE>
|
|
{
|
|
#[inline(always)]
|
|
pub const fn new_default(code: RawErrno) -> Self
|
|
{
|
|
Self(code)
|
|
}
|
|
}
|
|
|
|
impl<const BUFF_SIZE: usize> ErrnoFormatter<BUFF_SIZE>
|
|
{
|
|
#[inline(always)]
|
|
pub const fn new(code: RawErrno) -> Self
|
|
{
|
|
Self(code)
|
|
}
|
|
|
|
#[inline]
|
|
pub const fn raw(self) -> RawErrno
|
|
{
|
|
self.0
|
|
}
|
|
|
|
#[inline]
|
|
pub const fn buffer_size(self) -> usize
|
|
{
|
|
BUFF_SIZE
|
|
}
|
|
|
|
#[inline]
|
|
pub const fn with_buffer_size<const NBUFF_SIZE: usize>(self) -> ErrnoFormatter<NBUFF_SIZE>
|
|
{
|
|
ErrnoFormatter::<NBUFF_SIZE>::new(self.0)
|
|
}
|
|
|
|
pub fn try_to_string(&self) -> Result<String, Result<UString, RawErrno>>
|
|
{
|
|
match self.try_to_raw_string() {
|
|
Ok(string) => string.into_string().map_err(|us| Ok(us)),
|
|
Err(en) => Err(Err(en)),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn try_to_raw_string(&self) -> Result<UString, RawErrno>
|
|
{
|
|
errno_ustring_buffer::<BUFF_SIZE>(self.0)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn try_to_c_string(&self) -> Result<CString, RawErrno>
|
|
{
|
|
errno_cstring_buffer::<BUFF_SIZE>(self.0)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn to_raw_buffer(&self) -> Result<[u8; BUFF_SIZE], RawErrno>
|
|
{
|
|
errno_raw_buffer(self.0)
|
|
}
|
|
}
|
|
|
|
impl<const BUFF_SIZE: usize> fmt::Display for ErrnoFormatter<BUFF_SIZE>
|
|
{
|
|
#[inline]
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
errno_fmt_message::<BUFF_SIZE>(self.0, f)
|
|
}
|
|
}
|
|
|
|
|
|
fn errno_fmt_message<const BUFF_SIZE: usize>(code: RawErrno, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
if let Ok(buffer) = errno_raw_buffer::<BUFF_SIZE>(code)
|
|
{
|
|
if let Some(first) = buffer.split(|&b| b == 0).next()
|
|
{
|
|
f.write_str(String::from_utf8_lossy(first).as_ref())
|
|
} else {
|
|
#[cold]
|
|
#[inline(never)] //XXX: Should we noinline this cold path? I think we should.
|
|
fn _failed_path(code: RawErrno, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
write!(f, "<!unknown: empty message for errno {}>", code)
|
|
}
|
|
_failed_path(code, f)
|
|
}
|
|
} else {
|
|
#[cold]
|
|
#[inline(never)]
|
|
fn _failed_path(code: RawErrno, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
write!(f, "<!unknown: message extraction failed for errno {}>", code)
|
|
}
|
|
_failed_path(code, f)
|
|
}
|
|
}
|
|
|
|
fn errno_raw_buffer<const BUFF_SIZE: usize>(errno: RawErrno) -> Result<[u8; BUFF_SIZE], RawErrno>
|
|
{
|
|
use std::mem::MaybeUninit;
|
|
|
|
let mut buffer: MaybeUninit<[u8; BUFF_SIZE]> = MaybeUninit::uninit();
|
|
|
|
Ok(unsafe {
|
|
errno_if_nz(libc::strerror_r(errno, buffer.as_mut_ptr() as *mut raw::c_char, BUFF_SIZE))?;
|
|
|
|
buffer.assume_init()
|
|
})
|
|
}
|
|
|
|
fn errno_raw_ustr(errno: RawErrno, buffer: &mut [u8]) -> Result<&UStr, RawErrno>
|
|
{
|
|
unsafe {
|
|
errno_if_nz(libc::strerror_r(errno, buffer.as_mut_ptr() as *mut raw::c_char, buffer.len()))?;
|
|
}
|
|
Ok(UStr::new_mut(buffer))
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn errno_raw_cstr(errno: RawErrno, buffer: &mut [u8]) -> Result<&CStr, RawErrno>
|
|
{
|
|
errno_raw_ustr(errno, buffer).map(|x| unsafe {x.to_c_str()})
|
|
}
|
|
|
|
fn errno_ustring_buffer<const BUFF_SIZE: usize>(errno: RawErrno) -> Result<UString, RawErrno>
|
|
{
|
|
let buf = errno_raw_buffer::<BUFF_SIZE>(errno)?;
|
|
Ok(UString::new_from_buffer(&mut &buf[..], BUFF_SIZE))
|
|
}
|
|
|
|
fn errno_cstring_buffer<const BUFF_SIZE: usize>(errno: RawErrno) -> Result<CString, RawErrno>
|
|
{
|
|
errno_ustring_buffer::<BUFF_SIZE>(errno).map(|x| unsafe {x.to_c_string()})
|
|
}
|
|
|
|
fn errno_lossy_string_buffer<const BUFF_SIZE: usize>(errno: RawErrno) -> Result<String, RawErrno>
|
|
{
|
|
errno_ustring_buffer::<BUFF_SIZE>(errno).map(|x| x.to_string_lossy().into_owned())
|
|
}
|
|
|
|
/// An error when `dup()` or `dup2()`ing a stream.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
pub struct DupError(RawErrno);
|
|
|
|
impl SystemError for DupError
|
|
{
|
|
/// The `errno` code for this error.
|
|
#[inline]
|
|
fn code(&self) -> RawErrno
|
|
{
|
|
self.0
|
|
}
|
|
}
|
|
|
|
impl error::Error for DupError{}
|
|
impl fmt::Display for DupError
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
write!(f, "dup() failed with {}: ", self.0)?;
|
|
self.fmt_message(f)
|
|
}
|
|
}
|
|
|
|
|
|
/// Get the current thread's `errno` value
|
|
pub fn current_errno() -> RawErrno
|
|
{
|
|
unsafe {*libc::__errno_location()}
|
|
}
|
|
|
|
/// Check FFI function returns for errors and return the appropriate errno value if needed.
|
|
///
|
|
/// # Returns
|
|
/// If `chk(rv)` returns `true`, `Err(<current errno>)` is returned. Otherwise, `Ok(rv)` is returned.
|
|
#[inline(always)]
|
|
pub fn errno_if<T: Copy, F>(rv: T, chk: F) -> Result<T, RawErrno>
|
|
where F: Fn(T) -> bool
|
|
{
|
|
if !chk(rv) {
|
|
Ok(rv)
|
|
} else {
|
|
#[cold]
|
|
#[inline(never)]
|
|
fn _error() -> RawErrno
|
|
{
|
|
current_errno()
|
|
}
|
|
Err(_error())
|
|
}
|
|
}
|
|
#[inline(always)]
|
|
fn errno_if_not<const EXPECTED: raw::c_int>(rv: raw::c_int) -> Result<raw::c_int, RawErrno>
|
|
{
|
|
if rv == EXPECTED {
|
|
Ok(rv)
|
|
} else {
|
|
#[cold]
|
|
#[inline(never)]
|
|
fn _error() -> RawErrno
|
|
{
|
|
current_errno()
|
|
}
|
|
Err(_error())
|
|
}
|
|
}
|
|
#[inline(always)]
|
|
fn errno_if_nz(rv: raw::c_int) -> Result<(), RawErrno>
|
|
{
|
|
errno_if_not::<0>(rv).map(|_| ())
|
|
}
|
|
|
|
// Wrapper traits
|
|
|
|
//TODO: Move wrapper traits into a seperate module in src/loli/ to unclutter this file
|
|
|
|
/// Extension methods for `dup()`ing file descriptors
|
|
pub trait DupExt: Sized
|
|
{
|
|
unsafe fn dup(&self) -> Result<Self, DupError>;
|
|
}
|
|
|
|
/// Extension methods for `dup2()`ing file descriptors
|
|
pub trait Dup2Ext<U: ?Sized>
|
|
{
|
|
unsafe fn dup2(&self, other: &mut U) -> Result<(), DupError>;
|
|
}
|
|
|
|
impl<T: ?Sized, U: ?Sized> Dup2Ext<U> for T
|
|
where T: AsRawFd,
|
|
U: AsRawFd
|
|
{
|
|
unsafe fn dup2(&self, other: &mut U) -> Result<(), DupError>
|
|
{
|
|
errno_if(raw::dup2(self.as_raw_fd(), other.as_raw_fd()), |v| v < 0).map_err(DupError)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<T> DupExt for T
|
|
where T: FromRawFd + AsRawFd
|
|
{
|
|
unsafe fn dup(&self) -> Result<Self, DupError>
|
|
{
|
|
errno_if(raw::dup(self.as_raw_fd()), |v| v < 0)
|
|
.map(|fd| Self::from_raw_fd(fd)) // WHY are `unsafe fn`s not first class in an `unsafe` context??
|
|
.map_err(DupError)
|
|
}
|
|
}
|
|
|
|
//TODO: Document the following, and make sure it works like CStr/CString (minus the nul-termination bs)
|
|
|
|
//TODO: Move UStr/UString and related stuffs into a seperate module in src/loli/ to unclutter this file
|
|
|
|
|
|
/// A reference to an unmanaged string that may contain non-utf8 characters.
|
|
///
|
|
/// This differs from `CStr` in that it is **not** nul-terminated.
|
|
#[repr(transparent)]
|
|
pub struct UStr([u8]);
|
|
|
|
impl UStr {
|
|
#[inline(always)]
|
|
pub unsafe fn from_raw_mut<'a>(ptr: *mut u8, size: usize) -> &'a mut Self
|
|
{
|
|
&mut *(std::ptr::slice_from_raw_parts_mut::<u8>(ptr, size) as *mut Self)
|
|
}
|
|
#[inline(always)]
|
|
pub unsafe fn from_raw<'a>(ptr: *const u8, size: usize) -> &'a Self
|
|
{
|
|
&*(std::ptr::slice_from_raw_parts::<u8>(ptr, size) as *const Self)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn new<'a>(bytes: &'a [u8]) -> &'a Self
|
|
{
|
|
// SAFETY: ustr is transparent.
|
|
unsafe {
|
|
std::mem::transmute::<&'a [u8], &'a Self>(bytes)
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn new_mut<'a>(bytes: &'a mut [u8]) -> &'a mut Self
|
|
{
|
|
// SAFETY: ustr is transparent.
|
|
unsafe {
|
|
std::mem::transmute::<&'a mut [u8], &'a mut Self>(bytes)
|
|
}
|
|
}
|
|
|
|
/// Convert to a reference to a nul-terminated byte string
|
|
///
|
|
/// # Safety
|
|
/// The unmanaged string must contain a null byte in its slice.
|
|
pub unsafe fn to_c_str(&self) -> &CStr
|
|
{
|
|
CStr::from_ptr(self.0.as_ptr() as *const raw::c_char)
|
|
}
|
|
|
|
/// Create a `CStr` from this nul-terminated unmanaged byte string
|
|
///
|
|
/// # Returns
|
|
/// The final byte in the slice must be `\0`, or `Err` will be returned.
|
|
pub fn as_c_str(&self) -> Result<&CStr, std::ffi::FromBytesWithNulError>
|
|
{
|
|
CStr::from_bytes_with_nul(&self.0[..])
|
|
}
|
|
|
|
/// Create an unmanaged string from a nul-terminated string
|
|
///
|
|
/// # Nul-terminator
|
|
/// The returned unmanaged string reference will contain the nul-terminator as the final byte in the slice.
|
|
#[inline]
|
|
pub fn from_c_str(raw: &CStr) -> &Self
|
|
{
|
|
Self::new(raw.to_bytes_with_nul())
|
|
}
|
|
|
|
/// Returns `true` if the underlying slice is nul-terminated, and can be safely converted back/forth with `CStr`.
|
|
#[inline]
|
|
pub fn is_nul_terminated(&self) -> bool
|
|
{
|
|
self.0.last().map(|&x| x == 0).unwrap_or(false)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn to_str(&self) -> Result<&str, std::str::Utf8Error>
|
|
{
|
|
std::str::from_utf8(&self.0[..])
|
|
}
|
|
|
|
#[inline]
|
|
pub fn to_mut_str(&mut self) -> Result<&mut str, std::str::Utf8Error>
|
|
{
|
|
std::str::from_utf8_mut(&mut self.0[..])
|
|
}
|
|
|
|
pub fn to_string_lossy(&self) -> std::borrow::Cow<'_, str>
|
|
{
|
|
String::from_utf8_lossy(&self.0[..])
|
|
}
|
|
|
|
#[inline]
|
|
pub fn into_ustring(self: Box<Self>) -> UString
|
|
{
|
|
UString::from_boxed_ustr(self)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn from_ustring(owned: UString) -> Box<Self>
|
|
{
|
|
owned.into_boxed_ustr()
|
|
}
|
|
}
|
|
|
|
/// An owned unmanaged string that may contain non-utf8 characters.
|
|
///
|
|
/// This differs from `CString` in that it is **not** nul-terminated.
|
|
pub struct UString(Box<[u8]>);
|
|
|
|
impl UString
|
|
{
|
|
/// Create a new unmanaged string from `len` bytes from a buffer
|
|
pub fn new_from_buffer<B: Buf + ?Sized>(buffer: &mut B, len: usize) -> Self
|
|
{
|
|
let mut vec = Vec::with_capacity(len);
|
|
buffer.copy_to_slice(&mut vec[..]);
|
|
Self(vec.into())
|
|
}
|
|
|
|
pub fn new_from_bytes(bytes: impl Into<bytes::Bytes>) -> Self
|
|
{
|
|
Self::new_from_slice(bytes.into())
|
|
}
|
|
|
|
/// Create a new unmanaged string by copying it from a slice
|
|
#[inline]
|
|
pub fn new_from_slice(slice: impl AsRef<[u8]>) -> Self
|
|
{
|
|
Self::new(slice.as_ref().to_owned())
|
|
}
|
|
/// Create a new unmanaged string from a container containing arbitrary bytes
|
|
#[inline]
|
|
pub fn new(bytes: impl Into<Box<[u8]>>) -> Self
|
|
{
|
|
Self(bytes.into())
|
|
}
|
|
|
|
/// Create an owned (allocated) unmanaged string from a pointer and size
|
|
///
|
|
/// # Safety
|
|
/// `ptr` *must* have been originally created form a `Box::into_raw()` call.
|
|
#[inline]
|
|
pub unsafe fn from_raw(ptr: *mut u8, size: usize) -> Self
|
|
{
|
|
Self(Box::from_raw(std::ptr::slice_from_raw_parts_mut(ptr, size)))
|
|
}
|
|
|
|
#[inline]
|
|
pub fn into_raw(self) -> Box<[u8]>
|
|
{
|
|
self.0
|
|
}
|
|
|
|
pub fn to_mut_bytes(&self) -> bytes::BytesMut
|
|
{
|
|
bytes::BytesMut::from(&self.0[..])
|
|
}
|
|
|
|
pub fn into_bytes(self) -> bytes::Bytes
|
|
{
|
|
bytes::Bytes::from(self.0)
|
|
}
|
|
|
|
pub fn to_bytes(&self) -> bytes::Bytes
|
|
{
|
|
bytes::Bytes::copy_from_slice(&self.0[..])
|
|
}
|
|
|
|
pub fn into_boxed_ustr(self) -> Box<UStr>
|
|
{
|
|
unsafe {
|
|
Box::<UStr>::from_raw(Box::<[u8]>::into_raw(self.0) as *mut UStr)
|
|
}
|
|
}
|
|
|
|
pub fn from_boxed_ustr(us: Box<UStr>) -> Self
|
|
{
|
|
Self(unsafe {
|
|
Box::<[u8]>::from_raw(Box::<UStr>::into_raw(us) as *mut [u8])
|
|
})
|
|
}
|
|
|
|
pub fn into_string(self) -> Result<String, Self>
|
|
{
|
|
let vec = Vec::from(self.0);
|
|
String::from_utf8(vec).map_err(|e| {
|
|
Self(e.into_bytes().into())
|
|
})
|
|
}
|
|
|
|
pub fn try_into_string(self) -> Result<String, std::string::FromUtf8Error>
|
|
{
|
|
let vec = Vec::from(self.0);
|
|
String::from_utf8(vec)/*.map_err(|e| {
|
|
Self(e.into_bytes().into())
|
|
})*/
|
|
}
|
|
|
|
/// Allocate a new owned `CString` from this nul-terminated unmanaged string.
|
|
///
|
|
/// # Safety
|
|
/// The underlying slice *must* contain a null byte.
|
|
#[inline]
|
|
pub unsafe fn to_c_string(&self) -> CString
|
|
{
|
|
self.deref().to_c_str().into()
|
|
}
|
|
|
|
/// Convert this nul-terminated `UString` into a `CString`.
|
|
///
|
|
/// # Returns
|
|
/// If the last bytes of the sequence is not `\0`, `Err` is returned.
|
|
#[inline]
|
|
pub fn into_c_string(self) -> Result<CString, std::ffi::FromVecWithNulError>
|
|
{
|
|
CString::from_vec_with_nul(self.0.into())
|
|
}
|
|
|
|
/// Convert this owned `CString` into a nul-terminated `UString`.
|
|
#[inline]
|
|
pub fn from_c_string(cstring: CString) -> Self
|
|
{
|
|
Self(cstring.into_bytes_with_nul().into())
|
|
}
|
|
}
|
|
|
|
impl From<UString> for Box<UStr>
|
|
{
|
|
#[inline]
|
|
fn from(from: UString) -> Self
|
|
{
|
|
from.into_boxed_ustr()
|
|
}
|
|
}
|
|
|
|
impl From<Box<UStr>> for UString
|
|
{
|
|
#[inline]
|
|
fn from(from: Box<UStr>) -> Self
|
|
{
|
|
from.into_ustring()
|
|
}
|
|
}
|
|
|
|
impl AsRef<[u8]> for UStr
|
|
{
|
|
#[inline]
|
|
fn as_ref(&self) -> &[u8]
|
|
{
|
|
&self.0[..]
|
|
}
|
|
}
|
|
|
|
/* XXX: Is this needed?
|
|
impl Deref for ustr
|
|
{
|
|
type Target = [u8];
|
|
#[inline]
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0[..]
|
|
}
|
|
}
|
|
|
|
XXX: Is DerefMut needed, too?
|
|
XXX: What about AsMut? I think those two are unsafe, especially if the &mut UStr comes from an &mut str. There are unsafe methods for this anyway.
|
|
*/
|
|
|
|
impl AsRef<UStr> for UString
|
|
{
|
|
#[inline(always)]
|
|
fn as_ref(&self) -> &UStr
|
|
{
|
|
UStr::new(&self.0[..])
|
|
}
|
|
}
|
|
|
|
|
|
impl Deref for UString
|
|
{
|
|
type Target = UStr;
|
|
|
|
#[inline]
|
|
fn deref(&self) -> &Self::Target {
|
|
self.as_ref()
|
|
}
|
|
}
|
|
|
|
//Should we allow mutation of the `ustr` here? I think so, because we don't allow mutation of the underlying [u8]
|
|
impl AsMut<UStr> for UString
|
|
{
|
|
#[inline(always)]
|
|
fn as_mut(&mut self) -> &mut UStr
|
|
{
|
|
UStr::new_mut(&mut self.0[..])
|
|
}
|
|
}
|
|
impl DerefMut for UString
|
|
{
|
|
#[inline]
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
self.as_mut()
|
|
}
|
|
}
|
|
|
|
// UStr/ing <--?> CStr/ing
|
|
|
|
impl From<CString> for UString
|
|
{
|
|
#[inline]
|
|
fn from(from: CString) -> Self
|
|
{
|
|
Self::from_c_string(from)
|
|
}
|
|
}
|
|
|
|
impl<'a> From<&'a CStr> for &'a UStr
|
|
{
|
|
#[inline]
|
|
fn from(from: &'a CStr) -> Self
|
|
{
|
|
UStr::from_c_str(from)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<UString> for CString
|
|
{
|
|
type Error = std::ffi::FromVecWithNulError;
|
|
|
|
#[inline]
|
|
fn try_from(from: UString) -> Result<Self, Self::Error>
|
|
{
|
|
from.into_c_string()
|
|
}
|
|
}
|
|
|
|
impl<'a> TryFrom<&'a UStr> for &'a CStr
|
|
{
|
|
type Error = std::ffi::FromBytesWithNulError;
|
|
|
|
#[inline]
|
|
fn try_from(from: &'a UStr) -> Result<Self, Self::Error>
|
|
{
|
|
from.as_c_str()
|
|
}
|
|
}
|
|
|
|
// UStr/ing <--?> str/ing
|
|
|
|
impl From<String> for UString
|
|
{
|
|
#[inline]
|
|
fn from(from: String) -> Self
|
|
{
|
|
Self::new(from.into_bytes())
|
|
}
|
|
}
|
|
|
|
impl<'a> From<&'a str> for &'a UStr
|
|
{
|
|
#[inline]
|
|
fn from(from: &'a str) -> Self
|
|
{
|
|
UStr::new(from.as_bytes())
|
|
}
|
|
}
|
|
// No From<&'_ mut str> for &'_ mut UStr: The bytes belonging to the &'_ str may be modified in safe code if this were allowed.
|
|
|
|
impl TryFrom<UString> for String
|
|
{
|
|
type Error = std::string::FromUtf8Error;
|
|
|
|
#[inline]
|
|
fn try_from(from: UString) -> Result<Self, Self::Error>
|
|
{
|
|
from.try_into_string()
|
|
}
|
|
}
|
|
|
|
impl<'a> TryFrom<&'a UStr> for &'a str
|
|
{
|
|
type Error = std::str::Utf8Error;
|
|
|
|
#[inline]
|
|
fn try_from(from: &'a UStr) -> Result<Self, Self::Error>
|
|
{
|
|
from.to_str()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests
|
|
{
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn dup2()
|
|
{
|
|
use std::fs;
|
|
unsafe { std::io::stderr().dup2(&mut std::io::stdout()) }
|
|
.expect("Failed to route stdout to stderr");
|
|
|
|
let null = fs::OpenOptions::new()
|
|
.read(true)
|
|
.write(true)
|
|
.open("/dev/null").unwrap();
|
|
unsafe {
|
|
null.dup2(&mut std::io::stdin())
|
|
}.expect("Failed to shunt stdin to /dev/null");
|
|
|
|
unsafe {
|
|
null.dup2(&mut std::io::stderr())
|
|
}.expect("Failed to shunt stderr to /dev/null");
|
|
}
|
|
}
|