9 releases

0.2.7 Jan 20, 2022
0.2.6 Oct 18, 2021
0.1.0 Oct 13, 2021

#385 in Unix APIs

22 downloads per month
Used in stromatekt

MIT license

43KB
383 lines

unixstring codecov Crates.io Docs

UnixString is an FFI-friendly null-terminated byte string that may be constructed from a String, a CString, a PathBuf, an OsString or a collection of bytes.

An UnixString can then be converted into a slice of CStr, Path or OsStr in infallible and zero-cost operations.

Why?

UnixString aims to be useful in any scenario where you'd like to use FFI (specially with C) on Unix systems. If you have a PathBuf, for example, you can send that data to a libc function, such as stat, but you'd have to first allocate a CString (or something analogous) to do so.

The same is true with OsString and String because these three types are allowed to have internal zero bytes and are not null-terminated.

A UnixString is very close to what a CString is but with increased flexibility and usability. A CString cannot be changed or increased after instantited, while UnixString is growable through its push and push_bytes methods, somewhat similar to OsString.

A CString also does not have direct reference conversions to anything but &[u8] or &CStr, while UnixString has those and more (described below).

Obtaining references from an UnixString

Into Function Notes
&CStr UnixString::as_c_str Available through AsRef as well
&Path UnixString::as_path Available through AsRef as well
&str UnixString::as_str Fails if the bytes of the UnixString aren't valid UTF-8
&[u8] UnixString::as_bytes Returns the bytes of the UnixString without the null terminator
&[u8] UnixString::as_bytes_with_nul Returns the bytes of the UnixString with the null terminator
&OsStr UnixString::as_os_str Available through AsRef as well
* const c_char UnixString::as_ptr

Creating an UnixString

From Potential failure Trait impl Function
CString Infallible From UnixString::from_cstring
PathBuf Fails if contains an interior zero byte TryFrom UnixString::from_pathbuf
String Fails if contains an interior zero byte TryFrom UnixString::from_string
Vec<u8> Fails if contains an interior zero byte TryFrom UnixString::from_bytes
OsString Fails if contains an interior zero byte TryFrom UnixString::from_os_string
* const c_char Unsafe, see the docs for more info None UnixString::from_ptr

Converting from an UnixString

Into Function Notes
CString UnixString::into_cstring
PathBuf UnixString::into_pathbuf
OsString UnixString::into_os_string
String UnixString::into_string Fails if the UnixString's bytes are not valid UTF-8
String UnixString::into_string_lossy
String UnixString::to_string_lossy Non-moving version of UnixString::into_string_lossy
String UnixString::into_string_unchecked Unsafe: creates a String without checking if the bytes are valid UTF-8
Vec<u8> UnixString::into_bytes Returns the bytes of the UnixString without the null terminator
Vec<u8> UnixString::into_bytes_with_nul Returns the bytes of the UnixString with the null terminator

All of the above are also available through .into().

Examples

Creating an UnixString with bytes received through FFI

use libc::{c_char, getcwd};
use unixstring::UnixString;

fn main() {
    const PATH_SIZ: usize = 1024;
    let mut buf: [c_char; 1024] = [0; 1024];

    let ptr = &mut buf as *mut c_char;

    unsafe { getcwd(ptr, PATH_SIZ) };

    if ptr.is_null() {
        panic!("getcwd failed");
    }

    let unix_string = unsafe { UnixString::from_ptr(ptr as *const c_char) };

    assert_eq!(unix_string.as_path(), std::env::current_dir().unwrap())
}

Using an UnixString to send bytes through FFI

use std::{convert::TryFrom, env};

use unixstring::UnixString;

fn stat(path: &UnixString) -> std::io::Result<libc::stat> {
    // Safety: The all-zero byte-pattern is a valid `struct stat`
    let mut stat_buf = unsafe { std::mem::zeroed() };

    if -1 == unsafe { libc::lstat(path.as_ptr(), &mut stat_buf) } {
        let io_err = std::io::Error::last_os_error();
        Err(io_err)
    } else {
        Ok(stat_buf)
    }
}


fn main() -> std::io::Result<()>{
    for arg in env::args_os().map(UnixString::try_from).flatten() {
        let stat = stat(&arg)?;
        
        let size = stat.st_size;

        println!("{} occupies {} bytes.", arg.as_path().display(), size);
    }

    Ok(())
}

Dependencies

~44KB