blob: db46a77c95928e69d44e2710e521e656690b5dba [file]
// 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;
extern crate std;
use crate::crubit_cc_std_internal::conversion_function_helpers;
use crate::lossy_utf8::{debug_bytes, LossyUtf8Display};
use crate::std::string_view;
use alloc::string::String;
use alloc::vec::Vec;
use bridge_rust::{transmute_abi, 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;
use ctor::{Ctor, CtorNew, FnCtor, Infallible, PinnedDrop, RvalueReference};
use std::fmt::Display;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
/// 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 dependency 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_wrapper {
owned_cpp_string: NonNull<c_void>,
}
// SAFETY: the string is owned, and mutation is only allowed via `Pin<&mut Self>`.
unsafe impl Send for string_wrapper {}
// SAFETY: the string is owned, and mutation is only allowed via `Pin<&mut Self>`.
unsafe impl Sync for string_wrapper {}
impl string_wrapper {
pub fn as_slice(&self) -> &[u8] {
self.as_ref()
}
pub fn to_vec(&self) -> Vec<u8> {
self.as_slice().into()
}
pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> {
str::from_utf8(self.as_slice())
}
pub fn to_string(&self) -> Result<String, core::str::Utf8Error> {
self.to_str().map(String::from)
}
#[cfg(unix)]
pub fn as_os_str(&self) -> &std::ffi::OsStr {
std::ffi::OsStr::from_bytes(&self)
}
#[cfg(unix)]
pub fn to_os_string(&self) -> std::ffi::OsString {
self.as_os_str().into()
}
/// 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 strings that may contain
/// non-Unicode data. The Display implementation replaces invalid UTF-8 with the Unicode
/// replacement character (�), and is potentially lossy.
pub fn display(&self) -> impl Display + use<'_> {
LossyUtf8Display(self.as_slice())
}
}
impl PartialEq for string_wrapper {
fn eq(&self, other: &Self) -> bool {
// SAFETY: `owned_cpp_string` is guaranteed to be a non-null C++ allocated
// pointer to std::string.
unsafe {
conversion_function_helpers::StringEqual(
self.owned_cpp_string.as_ptr(),
other.owned_cpp_string.as_ptr(),
)
}
}
}
impl Eq for string_wrapper {}
impl Default for string_wrapper {
fn default() -> Self {
"".into()
}
}
impl Clone for string_wrapper {
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_wrapper {
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_wrapper {
fn from(s: String) -> Self {
s.as_bytes().into()
}
}
impl From<&String> for string_wrapper {
fn from(s: &String) -> Self {
s.as_bytes().into()
}
}
impl From<&Vec<u8>> for string_wrapper {
fn from(s: &Vec<u8>) -> Self {
s.as_slice().into()
}
}
impl From<&str> for string_wrapper {
fn from(s: &str) -> Self {
s.as_bytes().into()
}
}
impl From<&[u8]> for string_wrapper {
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_wrapper {
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 initialized. (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_wrapper {
fn as_ref(&self) -> &[u8] {
&*self
}
}
impl core::fmt::Debug for string_wrapper {
// 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_wrapper({:?})", self.as_slice())
}
}
// Allow converting a cc_std::std::string_wrapper 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::CppCoerce<*const forward_declare::Incomplete<StringSymbol, Crate>>
for &'a string_wrapper
{
fn cpp_coerce(self) -> *const forward_declare::Incomplete<StringSymbol, Crate> {
self.owned_cpp_string.as_ptr() as *const _ as *const _
}
}
impl<'a, Crate> forward_declare::CppCoerce<*mut forward_declare::Incomplete<StringSymbol, Crate>>
for &'a mut string_wrapper
{
fn cpp_coerce(self) -> *mut forward_declare::Incomplete<StringSymbol, Crate> {
self.owned_cpp_string.as_ptr() as *mut _
}
}
impl<'a> forward_declare::CppCoerce<*const string> for &'a string_wrapper {
fn cpp_coerce(self) -> *const string {
self.owned_cpp_string.as_ptr() as *const string
}
}
impl<'a> forward_declare::CppCoerce<*mut string> for &'a mut string_wrapper {
fn cpp_coerce(self) -> *mut string {
self.owned_cpp_string.as_ptr() as *mut string
}
}
impl<'a> forward_declare::CppCoerce<&'a string> for &'a string_wrapper {
fn cpp_coerce(self) -> &'a string {
unsafe { &*(self.owned_cpp_string.as_ptr() as *const string) }
}
}
impl<'a> forward_declare::CppCoerce<core::pin::Pin<&'a mut string>> for &'a mut string_wrapper {
fn cpp_coerce(self) -> core::pin::Pin<&'a mut string> {
unsafe {
core::pin::Pin::new_unchecked(&mut *(self.owned_cpp_string.as_ptr() as *mut string))
}
}
}
/// 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.
#[derive(Clone, Default)]
pub struct BoxedCppStringAbi;
unsafe impl CrubitAbi for BoxedCppStringAbi {
type Value = string_wrapper;
const SIZE: usize = core::mem::size_of::<*mut c_void>();
fn encode(self, value: Self::Value, encoder: &mut Encoder) {
transmute_abi().encode(ManuallyDrop::new(value).as_mut_void_ptr(), encoder);
}
unsafe fn decode(self, 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 { transmute_abi().decode(decoder) };
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_wrapper`.
// * `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_wrapper);
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) {
// SAFETY: `output` is a valid buffer for a `string_wrapper`.
unsafe {
let output = &mut *(output as *mut MaybeUninit<string_wrapper>);
output.as_mut_ptr().write(string_wrapper { owned_cpp_string: ptr });
}
} else {
panic!("Failed to create owned string");
}
}
/// A layout-compatible `std::string` wrapper.
///
/// # Differences from Rust String types
///
/// * **Rust `String`**:
/// - **Movability**: Rust's `String` is trivially relocatable (can be moved with `memcpy`).
/// `string` (wrapping C++ `std::string`) is **not** trivially relocatable. On some platforms
/// (e.g., `libstdc++` with Short String Optimization), `std::string` may contain
/// self-references (pointers pointing to its own internal buffer). Moving it in memory without
/// running its C++ move constructor would invalidate these pointers. Thus, in Rust, it is
/// `!Unpin` and must be pinned.
/// - **Layout**: Rust's `String` has a fixed layout of 3 fields (pointer, capacity, length).
/// `string` has a platform-dependent layout that matches C++ `std::string`.
///
/// * **Rust `Vec<u8>`**:
/// - **Movability and Layout**: Like `String`, `Vec<u8>` is trivially relocatable and has a fixed
/// Rust layout, unlike `string`.
/// - **Intent**: `Vec<u8>` is a general-purpose byte container, whereas `string` represents a C++
/// string, typically used for textual data (though C++ `std::string` can contain arbitrary
/// bytes including nulls).
///
/// * **Byte String types (e.g., `bstr::BStr`)**:
/// - **Movability and Layout**: These are slice types or wrapper types that are trivially
/// relocatable, unlike `string`.
/// - `string` is most similar to these in that it does not enforce UTF-8 validation, but it
/// retains the C++ layout and non-movability constraints.
///
/// # Examples
///
/// ```
/// use cpp_std::string;
/// use ctor::{emplace, CtorNew};
///
/// // Create a string using emplace! and CtorNew:
/// let my_string = emplace!(string::ctor_new("Hello, C++!"));
///
/// // Access it as a slice:
/// assert_eq!(my_string.as_slice(), b"Hello, C++!");
///
/// // Convert to Rust str (if valid UTF-8):
/// assert_eq!(my_string.to_str().unwrap(), "Hello, C++!");
/// ```
#[crubit_annotate::cpp_layout_equivalent(cpp_type = "::std::string", include_path = "<string>")]
#[cfg_attr(target_pointer_width = "64", repr(C, align(8)))]
#[cfg_attr(target_pointer_width = "32", repr(C, align(4)))]
#[allow(non_camel_case_types)]
#[ctor::recursively_pinned(PinnedDrop)]
pub struct string {
_opaque: core::cfg_select! {
any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "riscv64") => {
[u8; 24]
}
any(target_arch = "arm", target_arch = "x86") => [u8; 12],
_ => {
compile_error!("string is only supported on x86_64, aarch64, riscv64, arm, and x86")
}
},
_pinned: core::marker::PhantomPinned,
}
// SAFETY: the string is owned, and mutation is only allowed via `Pin<&mut Self>`.
unsafe impl Send for string {}
// SAFETY: the string is owned, and mutation is only allowed via `Pin<&mut Self>`.
unsafe impl Sync for string {}
impl PinnedDrop for string {
unsafe fn pinned_drop(self: core::pin::Pin<&mut Self>) {
// SAFETY: `self` is a valid, pinned reference to a `string`.
unsafe {
conversion_function_helpers::StringDestroyInPlace(
self.get_unchecked_mut() as *mut _ as *mut c_void
);
}
}
}
impl CtorNew<()> for string {
type CtorType = impl Ctor<Output = Self, Error = Infallible>;
type Error = Infallible;
fn ctor_new(_args: ()) -> Self::CtorType {
unsafe {
// SAFETY: `dest` is a valid, pinned reference to a `string`.
FnCtor::new(|dest| {
conversion_function_helpers::StringCreateDefaultInPlace(dest as *mut c_void);
})
}
}
}
impl<'a> CtorNew<&'a [u8]> for string {
type CtorType = impl Ctor<Output = Self, Error = Infallible> + use<'a>;
type Error = Infallible;
fn ctor_new(args: &'a [u8]) -> Self::CtorType {
// SAFETY: `dest` is a valid, pinned reference to a `string`.
unsafe {
FnCtor::new(move |dest| {
conversion_function_helpers::StringCreateFromBufferInPlace(
dest as *mut c_void,
args.as_ptr() as _,
args.len(),
);
})
}
}
}
impl<'a> CtorNew<&'a str> for string {
type CtorType = impl Ctor<Output = Self, Error = Infallible> + use<'a>;
type Error = Infallible;
fn ctor_new(args: &'a str) -> Self::CtorType {
Self::ctor_new(args.as_bytes())
}
}
impl<'a> CtorNew<&'a string> for string {
type CtorType = impl Ctor<Output = Self, Error = Infallible> + use<'a>;
type Error = Infallible;
fn ctor_new(args: &'a string) -> Self::CtorType {
// SAFETY: `dest` is a valid, pinned reference to a `string`.
unsafe {
FnCtor::new(move |dest| {
conversion_function_helpers::StringCreateCopyInPlace(
dest as *mut c_void,
args as *const string as *const c_void,
);
})
}
}
}
impl<'a> CtorNew<RvalueReference<'a, string>> for string {
type CtorType = impl Ctor<Output = Self, Error = Infallible> + use<'a>;
type Error = Infallible;
fn ctor_new(args: RvalueReference<'a, string>) -> Self::CtorType {
// SAFETY: `dest` is a valid, pinned reference to a `string`.
unsafe {
FnCtor::new(move |dest| {
let src = args.0.get_unchecked_mut() as *mut string;
conversion_function_helpers::StringCreateInPlace(
dest as *mut c_void,
src as *mut c_void,
);
})
}
}
}
impl string {
/// Returns a shared slice of the string's bytes.
pub fn as_slice(&self) -> &[u8] {
self
}
/// Returns a mutable slice of the string's bytes.
///
/// Since `string` is `!Unpin`, this requires pinning.
pub fn as_mut_slice(self: core::pin::Pin<&mut Self>) -> &mut [u8] {
// SAFETY: `self` is a valid, pinned reference to a `string`.
unsafe {
let ptr = self.get_unchecked_mut() as *mut string as *mut c_void;
let len = conversion_function_helpers::StringGetSize(ptr);
core::slice::from_raw_parts_mut(
conversion_function_helpers::StringGetMutData(ptr) as _,
len,
)
}
}
/// Returns a raw pointer to the C++ `std::string` object.
pub fn as_ptr(&self) -> *const Self {
self as *const Self
}
/// Returns a raw mutable pointer to the C++ `std::string` object.
pub fn as_mut_ptr(self: core::pin::Pin<&mut Self>) -> *mut Self {
// SAFETY: `self` is a valid, pinned reference to a `string`.
unsafe { self.get_unchecked_mut() as *mut Self }
}
/// Returns a copy of the string's bytes as a `Vec<u8>`.
pub fn to_vec(&self) -> Vec<u8> {
self.as_slice().into()
}
/// Yields a `&str` slice if the string contains valid UTF-8.
pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> {
core::str::from_utf8(self.as_slice())
}
/// Yields a Rust `String` if the string contains valid UTF-8.
pub fn to_string(&self) -> Result<String, core::str::Utf8Error> {
self.to_str().map(String::from)
}
/// Returns an object that implements `Display` for safely printing the string,
/// replacing invalid UTF-8 with the Unicode replacement character.
pub fn display(&self) -> impl Display + '_ {
LossyUtf8Display(self.as_slice())
}
}
impl Deref for string {
type Target = [u8];
fn deref(&self) -> &Self::Target {
// SAFETY: `self` is a valid, pinned reference to a `string`.
unsafe {
let ptr = self as *const string as *const c_void;
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 PartialEq for string {
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl Eq for string {}
impl PartialOrd for string {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for string {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.as_slice().cmp(other.as_slice())
}
}
impl core::hash::Hash for string {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.as_slice().hash(state);
}
}
// PartialEq impls for comparing to string_view, str, and [u8]
impl<'a> PartialEq<string_view<'a>> for string {
fn eq(&self, other: &string_view<'a>) -> bool {
self.as_slice() == other.as_bytes()
}
}
impl PartialEq<str> for string {
fn eq(&self, other: &str) -> bool {
self.as_slice() == other.as_bytes()
}
}
impl<'a> PartialEq<&'a str> for string {
fn eq(&self, other: &&'a str) -> bool {
self.as_slice() == other.as_bytes()
}
}
impl PartialEq<[u8]> for string {
fn eq(&self, other: &[u8]) -> bool {
self.as_slice() == other
}
}
impl<'a> PartialEq<&'a [u8]> for string {
fn eq(&self, other: &&'a [u8]) -> bool {
self.as_slice() == *other
}
}
impl core::fmt::Debug for string {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
debug_bytes(self.as_slice(), f)
}
}