Create custom cxx binding to Utf8CStr

This commit is contained in:
topjohnwu
2023-12-26 23:08:06 +08:00
parent 062e498bdd
commit 65207f96c8
18 changed files with 142 additions and 68 deletions

View File

@@ -6,10 +6,12 @@ use std::path::Path;
use std::str::{Utf8Chunks, Utf8Error};
use std::{fmt, mem, slice, str};
use crate::slice_from_ptr_mut;
use cxx::{type_id, ExternType};
use libc::c_char;
use thiserror::Error;
use crate::slice_from_ptr_mut;
// Utf8CStr types are UTF-8 validated and null terminated strings.
//
// Several Utf8CStr types:
@@ -380,6 +382,22 @@ impl DerefMut for Utf8CStr {
}
}
// Notice that we only implement ExternType on Utf8CStr *reference*
unsafe impl ExternType for &Utf8CStr {
type Id = type_id!("rust::Utf8CStr");
type Kind = cxx::kind::Trivial;
}
macro_rules! const_assert_eq {
($left:expr, $right:expr $(,)?) => {
const _: [(); $left] = [(); $right];
};
}
// Assert ABI layout
const_assert_eq!(mem::size_of::<&Utf8CStr>(), mem::size_of::<[usize; 2]>());
const_assert_eq!(mem::align_of::<&Utf8CStr>(), mem::align_of::<[usize; 2]>());
// File system path extensions types
#[repr(transparent)]

View File

@@ -10,7 +10,8 @@ use libc::mode_t;
use crate::logging::CxxResultExt;
pub(crate) use crate::xwrap::*;
use crate::{
clone_attr, fclone_attr, fd_path, map_fd, map_file, Directory, FsPath, Utf8CStr, Utf8CStrBufRef,
clone_attr, cstr, fclone_attr, fd_path, map_fd, map_file, slice_from_ptr, Directory, FsPath,
Utf8CStr, Utf8CStrBufRef,
};
pub(crate) fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize {
@@ -57,12 +58,8 @@ unsafe extern "C" fn frm_rf(fd: OwnedFd) -> bool {
inner(fd).is_ok()
}
pub(crate) fn map_file_for_cxx(path: &[u8], rw: bool) -> &'static mut [u8] {
unsafe {
map_file(Utf8CStr::from_bytes_unchecked(path), rw)
.log_cxx()
.unwrap_or(&mut [])
}
pub(crate) fn map_file_for_cxx(path: &Utf8CStr, rw: bool) -> &'static mut [u8] {
map_file(path, rw).log_cxx().unwrap_or(&mut [])
}
pub(crate) fn map_fd_for_cxx(fd: RawFd, sz: usize, rw: bool) -> &'static mut [u8] {
@@ -171,3 +168,18 @@ unsafe extern "C" fn fclone_attr_for_cxx(a: RawFd, b: RawFd) -> bool {
.log_cxx_with_msg(|w| w.write_str("fclone_attr failed"))
.is_ok()
}
#[export_name = "cxx$utf8str$new"]
unsafe extern "C" fn str_new(this: &mut &Utf8CStr, s: *const u8, len: usize) {
*this = Utf8CStr::from_bytes(slice_from_ptr(s, len)).unwrap_or(cstr!(""));
}
#[export_name = "cxx$utf8str$ptr"]
unsafe extern "C" fn str_ptr(this: &&Utf8CStr) -> *const u8 {
this.as_ptr().cast()
}
#[export_name = "cxx$utf8str$len"]
unsafe extern "C" fn str_len(this: &&Utf8CStr) -> usize {
this.len()
}

View File

@@ -189,7 +189,7 @@ sFILE make_file(FILE *fp) {
}
mmap_data::mmap_data(const char *name, bool rw) {
auto slice = rust::map_file(byte_view(name), rw);
auto slice = rust::map_file(name, rw);
if (!slice.empty()) {
_buf = slice.data();
_sz = slice.size();

View File

@@ -34,12 +34,17 @@ pub mod ffi {
unsafe extern "C++" {
include!("misc.hpp");
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"]
type Utf8CStrRef<'a> = &'a crate::cstr::Utf8CStr;
fn mut_u8_patch(buf: &mut [u8], from: &[u8], to: &[u8]) -> Vec<usize>;
}
extern "Rust" {
#[cxx_name = "log_with_rs"]
fn log_from_cxx(level: LogLevelCxx, msg: &[u8]);
fn log_from_cxx(level: LogLevelCxx, msg: Utf8CStrRef);
#[cxx_name = "set_log_level_state"]
fn set_log_level_state_cxx(level: LogLevelCxx, enabled: bool);
fn exit_on_error(b: bool);
@@ -53,7 +58,7 @@ pub mod ffi {
#[cxx_name = "fd_path"]
fn fd_path_for_cxx(fd: i32, buf: &mut [u8]) -> isize;
#[cxx_name = "map_file"]
fn map_file_for_cxx(path: &[u8], rw: bool) -> &'static mut [u8];
fn map_file_for_cxx(path: Utf8CStrRef, rw: bool) -> &'static mut [u8];
#[cxx_name = "map_fd"]
fn map_fd_for_cxx(fd: i32, sz: usize, rw: bool) -> &'static mut [u8];
}

View File

@@ -15,7 +15,7 @@ static int fmt_and_log_with_rs(LogLevel level, const char *fmt, va_list ap) {
buf[0] = '\0';
// Fortify logs when a fatal error occurs. Do not run through fortify again
int len = std::min(__call_bypassing_fortify(vsnprintf)(buf, sz, fmt, ap), sz - 1);
log_with_rs(level, byte_view(buf, len + 1));
log_with_rs(level, rust::Utf8CStr(buf, len + 1));
return len;
}

View File

@@ -101,10 +101,8 @@ fn log_with_writer<F: FnOnce(LogWriter)>(level: LogLevel, f: F) {
}
}
pub fn log_from_cxx(level: LogLevelCxx, msg: &[u8]) {
pub fn log_from_cxx(level: LogLevelCxx, msg: &Utf8CStr) {
if let Some(level) = LogLevel::from_i32(level.repr) {
// SAFETY: The null termination is handled on the C++ side
let msg = unsafe { Utf8CStr::from_bytes_unchecked(msg) };
log_with_writer(level, |write| write(level, msg));
}
}

View File

@@ -274,3 +274,19 @@ int ssprintf(char *dest, size_t size, const char *fmt, ...) {
size_t strscpy(char *dest, const char *src, size_t size) {
return std::min(strlcpy(dest, src, size), size - 1);
}
extern "C" void cxx$utf8str$new(rust::Utf8CStr *self, const void *s, size_t len);
extern "C" const char *cxx$utf8str$ptr(const rust::Utf8CStr *self);
extern "C" size_t cxx$utf8str$len(const rust::Utf8CStr *self);
rust::Utf8CStr::Utf8CStr(const char *s, size_t len) {
cxx$utf8str$new(this, s, len);
}
const char *rust::Utf8CStr::data() const {
return cxx$utf8str$ptr(this);
}
size_t rust::Utf8CStr::length() const {
return cxx$utf8str$len(this);
}

View File

@@ -331,3 +331,30 @@ constexpr auto operator+(T e) noexcept ->
std::enable_if_t<std::is_enum<T>::value, std::underlying_type_t<T>> {
return static_cast<std::underlying_type_t<T>>(e);
}
namespace rust {
struct Utf8CStr {
const char *data() const;
size_t length() const;
Utf8CStr(const char *s, size_t len);
Utf8CStr() : Utf8CStr("", 1) {};
Utf8CStr(const Utf8CStr &o) = default;
Utf8CStr(Utf8CStr &&o) = default;
Utf8CStr(const char *s) : Utf8CStr(s, strlen(s) + 1) {};
Utf8CStr(std::string_view s) : Utf8CStr(s.data(), s.length() + 1) {};
Utf8CStr(std::string s) : Utf8CStr(s.data(), s.length() + 1) {};
const char *c_str() const { return this->data(); }
size_t size() const { return this->length(); }
bool empty() const { return this->length() == 0 ; }
operator std::string_view() { return {data(), length()}; }
private:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-private-field"
std::array<std::uintptr_t, 2> repr;
#pragma clang diagnostic pop
};
} // namespace rust