Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/vrmiguel/unixstring

An FFI-friendly null-terminated byte string
https://github.com/vrmiguel/unixstring

ffi hacktoberfest rust unix

Last synced: 3 months ago
JSON representation

An FFI-friendly null-terminated byte string

Awesome Lists containing this project

README

        

# unixstring [![codecov](https://codecov.io/gh/vrmiguel/unixstring/branch/master/graph/badge.svg?token=6rvhsF5Eiq)](https://codecov.io/gh/vrmiguel/unixstring) ![Crates.io](https://img.shields.io/crates/v/unixstring) [![Docs](https://img.shields.io/badge/docs.rs-unixstring-green)](https://docs.rs/unixstring/)

`UnixString` is an FFI-friendly null-terminated byte string that may be constructed from a [`String`](https://doc.rust-lang.org/std/string/struct.String.html), a [`CString`](https://doc.rust-lang.org/std/ffi/struct.CString.html), a [`PathBuf`](https://doc.rust-lang.org/std/path/struct.PathBuf.html), an [`OsString`](https://doc.rust-lang.org/std/ffi/struct.OsString.html) or a collection of bytes.

An [`UnixString`](UnixString) can then be converted into a slice of [`CStr`](https://doc.rust-lang.org/std/ffi/struct.CStr.html), [`Path`](https://doc.rust-lang.org/std/path/struct.Path.html) or [`OsStr`](https://doc.rust-lang.org/std/ffi/struct.OsStr.html) 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` | 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` | `UnixString::into_bytes` | Returns the bytes of the `UnixString` without the null terminator |
| `Vec` | `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

```rust=
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

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

use unixstring::UnixString;

fn stat(path: &UnixString) -> std::io::Result {
// 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(())
}
```