| // 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"); | |
| } | |
| } |