blob: 4b2a43bc877f3b8f455eb4fc8c0cdba389da63d5 [file] [log] [blame] [edit]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
extern crate alloc;
use crate::crubit_cc_std_internal::conversion_function_helpers;
use alloc::string::String;
use alloc::vec::Vec;
use bridge_rust::{CrubitAbi, Decoder, Encoder};
use core::clone::Clone;
use core::cmp::Eq;
use core::cmp::PartialEq;
use core::ffi::c_void;
use core::mem::{ManuallyDrop, MaybeUninit};
use core::ops::Deref;
use core::ptr::NonNull;
/// An owned C++ string. The pointer is guaranteed to be a non-null C++
/// allocated pointer to std::string.
///
/// We make sure that conversion between `string` and `std::string` is O(1), but
/// conversion between `string` and other types like `&str` or `String` can be
/// O(n).
// TODO: Make it mutable?.
// TODO: add a depedency on `crubit_annotate` to use that directly rather than doc attributes.
#[doc = "CRUBIT_ANNOTATE: cpp_type=std::string"]
#[doc = "CRUBIT_ANNOTATE: include_path=<string>"]
#[doc = "CRUBIT_ANNOTATE: cpp_to_rust_converter=cpp_string_to_rust_string"]
#[doc = "CRUBIT_ANNOTATE: rust_to_cpp_converter=rust_string_to_cpp_string"]
#[allow(non_snake_case)]
#[repr(C)]
pub struct string {
owned_cpp_string: NonNull<c_void>,
}
impl string {
pub fn as_slice(&self) -> &[u8] {
self.as_ref()
}
/// Returns a `*const c_void` pointing to the underlying C++
/// `std::string` object.
///
/// The caller must ensure that the `string` outlives the pointer.
///
/// This method guarantees that for the purpose of the aliasing
/// model, this method does not materialize a reference to the
/// underlying data the string owns, and thus the returned
/// pointer will remain valid when mixed with other calls to
/// `as_void_ptr` and `as_mut_void_ptr`.
pub fn as_void_ptr(&self) -> *const c_void {
self.owned_cpp_string.as_ptr() as *const _
}
/// Returns a `*mut c_void` pointing to the underlying C++
/// `std::string` object.
///
/// The caller must ensure that the `string` outlives the pointer.
///
/// This method guarantees that for the purpose of the aliasing
/// model, this method does not materialize a reference to the
/// underlying data the string owns, and thus the returned
/// pointer will remain valid when mixed with other calls to
/// `as_void_ptr` and `as_mut_void_ptr`.
///
/// However, note that writing to the pointer will invalidate references to
/// the underlying data. For example, the behavior is undefined if the
/// underlying string is mutated through this pointer during the lifetime
/// of a slice as returned by `as_slice`.
pub fn as_mut_void_ptr(&mut self) -> *mut c_void {
self.owned_cpp_string.as_ptr()
}
/// Returns an object that implements `Display` for safely printing paths that may contain
/// non-Unicode data. This may perform lossy conversion, depending on the underlying data.
pub fn display(&self) -> Display<'_> {
Display(self.as_slice())
}
}
impl PartialEq for string {
fn eq(&self, other: &Self) -> bool {
unsafe {
// SAFETY: `owned_cpp_string` is guaranteed to be a non-null C++ allocated
// pointer to std::string.
conversion_function_helpers::StringEqual(
self.owned_cpp_string.as_ptr(),
other.owned_cpp_string.as_ptr(),
)
}
}
}
impl Eq for string {}
impl Default for string {
fn default() -> Self {
"".into()
}
}
impl Clone for string {
fn clone(&self) -> Self {
// SAFETY: `owned_cpp_string` is guaranteed to be a non-null C++ allocated
// pointer to std::string.
let raw_string = unsafe {
conversion_function_helpers::StringCopyOwnedPtr(self.owned_cpp_string.as_ptr())
};
if let Some(ptr) = NonNull::new(raw_string) {
Self { owned_cpp_string: ptr }
} else {
panic!("Failed to copy string");
}
}
}
impl Drop for string {
fn drop(&mut self) {
unsafe {
// SAFETY: `owned_cpp_string` is guaranteed to be a non-null C++ allocated
// pointer to std::string.
conversion_function_helpers::StringDelete(self.owned_cpp_string.as_ptr());
}
}
}
impl From<String> for string {
fn from(s: String) -> Self {
s.as_bytes().into()
}
}
impl From<&String> for string {
fn from(s: &String) -> Self {
s.as_bytes().into()
}
}
impl From<&Vec<u8>> for string {
fn from(s: &Vec<u8>) -> Self {
s.as_slice().into()
}
}
impl From<&str> for string {
fn from(s: &str) -> Self {
s.as_bytes().into()
}
}
impl From<&[u8]> for string {
fn from(s: &[u8]) -> Self {
// SAFETY: Rust slice returns a valid pointer to a buffer of bytes.
let raw_string = unsafe {
conversion_function_helpers::StringCreateFromBuffer(s.as_ptr() as _, s.len())
};
if let Some(ptr) = NonNull::new(raw_string) {
Self { owned_cpp_string: ptr }
} else {
panic!("Failed to create string");
}
}
}
impl Deref for string {
type Target = [u8];
fn deref(&self) -> &Self::Target {
let ptr = self.owned_cpp_string.as_ptr();
// SAFETY:
//
// * `owned_cpp_string` is guaranteed to be a non-null C++ allocated pointer to
// std::string so `ptr` is non-null.
// * `StringGetData` returns the pointer of the C++ `std::string::data()`, which
// is guaranteed to be non-null and point to a continuous memory region. Every
// byte in [ptr, ptr + len)) is intialized. (See https://en.cppreference.com/w/cpp/string/basic_string/data)
// * The data is guaranteed to be not mutated because we don't ever mutate
// data() except when accessed via &mut self, which is blocked by Rust borrow
// checker.
// * `len` is guaranteed to be less than `isize::MAX` because C++
// implementations guarantee in practice that the object won't go past the end
// of the address space.
unsafe {
let len = conversion_function_helpers::StringGetSize(ptr);
core::slice::from_raw_parts(conversion_function_helpers::StringGetData(ptr) as _, len)
}
}
}
impl core::convert::AsRef<[u8]> for string {
fn as_ref(&self) -> &[u8] {
&*self
}
}
impl core::fmt::Debug for string {
// TODO(b/351976622): Make a pretty Debug like std::string(b"\xffhello\xde") or
// similar.
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "cc_std::string({:?})", self.as_slice())
}
}
impl core::fmt::Display for string {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.display().fmt(f)
}
}
// Allow converting a cc_std::std::string reference to a "real" C++ string pointer.
type StringSymbol = forward_declare::symbol!(
"std :: basic_string < char , std :: char_traits < char >, std :: allocator < char >>"
);
impl<'a, Crate> forward_declare::CppCast<*const forward_declare::Incomplete<StringSymbol, Crate>>
for &'a string
{
fn cpp_cast(self) -> *const forward_declare::Incomplete<StringSymbol, Crate> {
self.owned_cpp_string.as_ptr() as *const _ as *const _
}
}
impl<'a, Crate> forward_declare::CppCast<*mut forward_declare::Incomplete<StringSymbol, Crate>>
for &'a mut string
{
fn cpp_cast(self) -> *mut forward_declare::Incomplete<StringSymbol, Crate> {
self.owned_cpp_string.as_ptr() as *mut _
}
}
/// Helper struct for safely printing C++ string data with `format!` and `{}`.
///
/// A string from C++ might contain non-Unicode data. This struct implements the Display trait in a
/// way that mitigates that. It is created by the display method on `string`. This may perform lossy
/// conversion, depending on the underlying data.
pub struct Display<'a>(&'a [u8]);
impl<'a> core::fmt::Display for Display<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use core::fmt::Write;
for chunk in self.0.utf8_chunks() {
f.write_str(chunk.valid())?;
if !chunk.invalid().is_empty() {
f.write_char(core::char::REPLACEMENT_CHARACTER)?;
}
}
Ok(())
}
}
/// The Crubit ABI for C++ `std::string`. It is specified as one pointer.
///
/// This pointer should point to a heap allocated `std::string` object, where the pointer is the
/// owner of the allocation. Alternatively, the pointer can be null if and only if the allocation
/// failed, in which case it's okay to panic.
pub struct BoxedCppStringAbi;
unsafe impl CrubitAbi for BoxedCppStringAbi {
type Value = string;
const SIZE: usize = core::mem::size_of::<*mut c_void>();
fn encode(value: Self::Value, encoder: &mut Encoder) {
encoder.encode_transmute(ManuallyDrop::new(value).as_mut_void_ptr());
}
unsafe fn decode(decoder: &mut Decoder) -> Self::Value {
// SAFETY: the caller guarantees that the buffer contains an allocated or null pointer to a
// C++ `std::string` object.
let ptr: *mut c_void = unsafe { decoder.decode_transmute() };
Self::Value {
owned_cpp_string: NonNull::new(ptr).expect("Boxing a std::string shouldn't fail"),
}
}
}
// Void pointer converters are needed for cc_bindings_from_rs.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rust_string_to_cpp_string(input: *const c_void, output: *mut c_void) {
// SAFETY:
// * `input` is a valid `string`.
// * `input.owned_cpp_string` is guaranteed to be a non-null C++ allocated
// pointer to std::string.
// * `output` is a valid C++ `std::string`.
unsafe {
let input = &*(input as *const string);
conversion_function_helpers::StringCreateInPlace(output, input.owned_cpp_string.as_ptr());
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cpp_string_to_rust_string(input: *mut c_void, output: *mut c_void) {
// SAFETY: `input` is a valid `std::string` so it can be safely moved.
let owned_cpp_string = unsafe { conversion_function_helpers::StringMoveOwnedPtr(input) };
if let Some(ptr) = NonNull::new(owned_cpp_string) {
let output = &mut *(output as *mut MaybeUninit<string>);
output.as_mut_ptr().write(string { owned_cpp_string: ptr });
} else {
panic!("Failed to create owned string");
}
}