diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/c_char.rs | 69 | ||||
-rw-r--r-- | src/cxx.cc | 89 | ||||
-rw-r--r-- | src/cxx_string.rs | 9 | ||||
-rw-r--r-- | src/cxx_vector.rs | 10 | ||||
-rw-r--r-- | src/exception.rs | 5 | ||||
-rw-r--r-- | src/extern_type.rs | 8 | ||||
-rw-r--r-- | src/fmt.rs | 2 | ||||
-rw-r--r-- | src/hash.rs | 12 | ||||
-rw-r--r-- | src/lib.rs | 55 | ||||
-rw-r--r-- | src/lossy.rs | 67 | ||||
-rw-r--r-- | src/result.rs | 7 | ||||
-rw-r--r-- | src/rust_string.rs | 1 | ||||
-rw-r--r-- | src/rust_vec.rs | 5 | ||||
-rw-r--r-- | src/shared_ptr.rs | 10 | ||||
-rw-r--r-- | src/sip.rs | 228 | ||||
-rw-r--r-- | src/symbols/exception.rs | 14 | ||||
-rw-r--r-- | src/symbols/rust_str.rs | 2 | ||||
-rw-r--r-- | src/symbols/rust_string.rs | 26 | ||||
-rw-r--r-- | src/symbols/rust_vec.rs | 10 | ||||
-rw-r--r-- | src/unique_ptr.rs | 16 | ||||
-rw-r--r-- | src/unwind.rs | 47 | ||||
-rw-r--r-- | src/weak_ptr.rs | 6 |
22 files changed, 630 insertions, 68 deletions
diff --git a/src/c_char.rs b/src/c_char.rs new file mode 100644 index 00000000..333d8491 --- /dev/null +++ b/src/c_char.rs @@ -0,0 +1,69 @@ +#[allow(missing_docs)] +pub type c_char = c_char_definition::c_char; + +// Validate that our definition is consistent with libstd's definition, without +// introducing a dependency on libstd in ordinary builds. +#[cfg(all(test, feature = "std"))] +const _: self::c_char = 0 as std::os::raw::c_char; + +#[allow(dead_code)] +mod c_char_definition { + // These are the targets on which c_char is unsigned. + #[cfg(any( + all( + target_os = "linux", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "hexagon", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "riscv64", + target_arch = "riscv32" + ) + ), + all( + target_os = "android", + any(target_arch = "aarch64", target_arch = "arm") + ), + all(target_os = "l4re", target_arch = "x86_64"), + all( + target_os = "freebsd", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "riscv64" + ) + ), + all( + target_os = "netbsd", + any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") + ), + all(target_os = "openbsd", target_arch = "aarch64"), + all( + target_os = "vxworks", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc64", + target_arch = "powerpc" + ) + ), + all(target_os = "fuchsia", target_arch = "aarch64") + ))] + pub use self::unsigned::c_char; + + // On every other target, c_char is signed. + pub use self::signed::*; + + mod unsigned { + pub type c_char = u8; + } + + mod signed { + pub type c_char = i8; + } +} @@ -40,8 +40,12 @@ void cxxbridge1$string$clone(rust::String *self, const rust::String &other) noexcept; bool cxxbridge1$string$from_utf8(rust::String *self, const char *ptr, std::size_t len) noexcept; +void cxxbridge1$string$from_utf8_lossy(rust::String *self, const char *ptr, + std::size_t len) noexcept; bool cxxbridge1$string$from_utf16(rust::String *self, const char16_t *ptr, std::size_t len) noexcept; +void cxxbridge1$string$from_utf16_lossy(rust::String *self, const char16_t *ptr, + std::size_t len) noexcept; void cxxbridge1$string$drop(rust::String *self) noexcept; const char *cxxbridge1$string$ptr(const rust::String *self) noexcept; std::size_t cxxbridge1$string$len(const rust::String *self) noexcept; @@ -81,6 +85,12 @@ void panic [[noreturn]] (const char *msg) { template void panic<std::out_of_range> [[noreturn]] (const char *msg); +template <typename T> +static bool is_aligned(const void *ptr) noexcept { + auto iptr = reinterpret_cast<std::uintptr_t>(ptr); + return !(iptr % alignof(T)); +} + String::String() noexcept { cxxbridge1$string$new(this); } String::String(const String &other) noexcept { @@ -121,17 +131,60 @@ String::String(const char *s, std::size_t len) { String::String(const char16_t *s) { assert(s != nullptr); + assert(is_aligned<char16_t>(s)); initString(this, s, std::char_traits<char16_t>::length(s)); } String::String(const char16_t *s, std::size_t len) { assert(s != nullptr || len == 0); + assert(is_aligned<char16_t>(s)); initString(this, s == nullptr && len == 0 ? reinterpret_cast<const char16_t *>(2) : s, len); } +struct String::lossy_t {}; + +String::String(lossy_t, const char *s, std::size_t len) noexcept { + cxxbridge1$string$from_utf8_lossy( + this, s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s, + len); +} + +String::String(lossy_t, const char16_t *s, std::size_t len) noexcept { + cxxbridge1$string$from_utf16_lossy( + this, + s == nullptr && len == 0 ? reinterpret_cast<const char16_t *>(2) : s, + len); +} + +String String::lossy(const std::string &s) noexcept { + return String::lossy(s.data(), s.length()); +} + +String String::lossy(const char *s) noexcept { + assert(s != nullptr); + return String::lossy(s, std::strlen(s)); +} + +String String::lossy(const char *s, std::size_t len) noexcept { + assert(s != nullptr || len == 0); + return String(lossy_t{}, s, len); +} + +String String::lossy(const char16_t *s) noexcept { + assert(s != nullptr); + assert(is_aligned<char16_t>(s)); + return String(lossy_t{}, s, std::char_traits<char16_t>::length(s)); +} + +String String::lossy(const char16_t *s, std::size_t len) noexcept { + assert(s != nullptr || len == 0); + assert(is_aligned<char16_t>(s)); + return String(lossy_t{}, s, len); +} + String &String::operator=(const String &other) &noexcept { if (this != &other) { cxxbridge1$string$drop(this); @@ -453,6 +506,17 @@ union MaybeUninit { }; } // namespace +namespace repr { +struct PtrLen final { + void *ptr; + std::size_t len; +}; +} // namespace repr + +extern "C" { +repr::PtrLen cxxbridge1$exception(const char *, std::size_t len) noexcept; +} + namespace detail { // On some platforms size_t is the same C++ type as one of the sized integer // types; on others it is a distinct type. Only in the latter case do we need to @@ -466,6 +530,23 @@ using isize_if_unique = typename std::conditional<std::is_same<rust::isize, int64_t>::value || std::is_same<rust::isize, int32_t>::value, struct isize_ignore, rust::isize>::type; + +class Fail final { + repr::PtrLen &throw$; + +public: + Fail(repr::PtrLen &throw$) noexcept : throw$(throw$) {} + void operator()(const char *) noexcept; + void operator()(const std::string &) noexcept; +}; + +void Fail::operator()(const char *catch$) noexcept { + throw$ = cxxbridge1$exception(catch$, std::strlen(catch$)); +} + +void Fail::operator()(const std::string &catch$) noexcept { + throw$ = cxxbridge1$exception(catch$.data(), catch$.length()); +} } // namespace detail } // namespace cxxbridge1 @@ -568,7 +649,9 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), void cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total( \ rust::Vec<CXX_TYPE> *ptr, std::size_t new_cap) noexcept; \ void cxxbridge1$rust_vec$##RUST_TYPE##$set_len(rust::Vec<CXX_TYPE> *ptr, \ - std::size_t len) noexcept; + std::size_t len) noexcept; \ + void cxxbridge1$rust_vec$##RUST_TYPE##$truncate(rust::Vec<CXX_TYPE> *ptr, \ + std::size_t len) noexcept; #define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE) \ template <> \ @@ -598,6 +681,10 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), template <> \ void Vec<CXX_TYPE>::set_len(std::size_t len) noexcept { \ cxxbridge1$rust_vec$##RUST_TYPE##$set_len(this, len); \ + } \ + template <> \ + void Vec<CXX_TYPE>::truncate(std::size_t len) { \ + cxxbridge1$rust_vec$##RUST_TYPE##$truncate(this, len); \ } #define SHARED_PTR_OPS(RUST_TYPE, CXX_TYPE) \ diff --git a/src/cxx_string.rs b/src/cxx_string.rs index 626c2c85..9ecbcc64 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -1,5 +1,8 @@ use crate::actually_private::Private; +use crate::lossy; +#[cfg(feature = "alloc")] use alloc::borrow::Cow; +#[cfg(feature = "alloc")] use alloc::string::String; use core::cmp::Ordering; use core::fmt::{self, Debug, Display}; @@ -144,6 +147,8 @@ impl CxxString { /// Cow::Owned String. /// /// [replacement character]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] pub fn to_string_lossy(&self) -> Cow<str> { String::from_utf8_lossy(self.as_bytes()) } @@ -204,13 +209,13 @@ impl CxxString { impl Display for CxxString { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(self.to_string_lossy().as_ref(), f) + lossy::display(self.as_bytes(), f) } } impl Debug for CxxString { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Debug::fmt(self.to_string_lossy().as_ref(), f) + lossy::debug(self.as_bytes(), f) } } diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index d1fa23a2..abf9297a 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -368,7 +368,6 @@ pub unsafe trait VectorElement: Sized { macro_rules! vector_element_by_value_methods { (opaque, $segment:expr, $ty:ty) => {}; (trivial, $segment:expr, $ty:ty) => { - #[doc(hidden)] unsafe fn __push_back(v: Pin<&mut CxxVector<$ty>>, value: &mut ManuallyDrop<$ty>) { extern "C" { attr! { @@ -378,7 +377,6 @@ macro_rules! vector_element_by_value_methods { } unsafe { __push_back(v, value) } } - #[doc(hidden)] unsafe fn __pop_back(v: Pin<&mut CxxVector<$ty>>, out: &mut MaybeUninit<$ty>) { extern "C" { attr! { @@ -397,11 +395,9 @@ macro_rules! impl_vector_element { const_assert_eq!(1, mem::align_of::<CxxVector<$ty>>()); unsafe impl VectorElement for $ty { - #[doc(hidden)] fn __typename(f: &mut fmt::Formatter) -> fmt::Result { f.write_str($name) } - #[doc(hidden)] fn __vector_size(v: &CxxVector<$ty>) -> usize { extern "C" { attr! { @@ -411,7 +407,6 @@ macro_rules! impl_vector_element { } unsafe { __vector_size(v) } } - #[doc(hidden)] unsafe fn __get_unchecked(v: *mut CxxVector<$ty>, pos: usize) -> *mut $ty { extern "C" { attr! { @@ -422,7 +417,6 @@ macro_rules! impl_vector_element { unsafe { __get_unchecked(v, pos) } } vector_element_by_value_methods!($kind, $segment, $ty); - #[doc(hidden)] fn __unique_ptr_null() -> MaybeUninit<*mut c_void> { extern "C" { attr! { @@ -434,7 +428,6 @@ macro_rules! impl_vector_element { unsafe { __unique_ptr_null(&mut repr) } repr } - #[doc(hidden)] unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> MaybeUninit<*mut c_void> { extern "C" { attr! { @@ -446,7 +439,6 @@ macro_rules! impl_vector_element { unsafe { __unique_ptr_raw(&mut repr, raw) } repr } - #[doc(hidden)] unsafe fn __unique_ptr_get(repr: MaybeUninit<*mut c_void>) -> *const CxxVector<Self> { extern "C" { attr! { @@ -456,7 +448,6 @@ macro_rules! impl_vector_element { } unsafe { __unique_ptr_get(&repr) } } - #[doc(hidden)] unsafe fn __unique_ptr_release(mut repr: MaybeUninit<*mut c_void>) -> *mut CxxVector<Self> { extern "C" { attr! { @@ -466,7 +457,6 @@ macro_rules! impl_vector_element { } unsafe { __unique_ptr_release(&mut repr) } } - #[doc(hidden)] unsafe fn __unique_ptr_drop(mut repr: MaybeUninit<*mut c_void>) { extern "C" { attr! { diff --git a/src/exception.rs b/src/exception.rs index 2ae470e7..259b27d4 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,7 +1,10 @@ +#![cfg(feature = "alloc")] + use alloc::boxed::Box; use core::fmt::{self, Display}; /// Exception thrown from an `extern "C++"` function. +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[derive(Debug)] pub struct Exception { pub(crate) what: Box<str>, @@ -13,6 +16,8 @@ impl Display for Exception { } } +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] impl std::error::Error for Exception {} impl Exception { diff --git a/src/extern_type.rs b/src/extern_type.rs index 8c9c2860..d131ae12 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -1,5 +1,6 @@ use self::kind::{Kind, Opaque, Trivial}; use crate::CxxString; +#[cfg(feature = "alloc")] use alloc::string::String; /// A type for which the layout is determined by its C++ definition. @@ -186,9 +187,11 @@ pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {} pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {} macro_rules! impl_extern_type { - ($([$kind:ident] $($ty:path = $cxxpath:literal)*)*) => { + ($([$kind:ident] $($(#[$($attr:tt)*])* $ty:path = $cxxpath:literal)*)*) => { $($( + $(#[$($attr)*])* unsafe impl ExternType for $ty { + #[allow(unused_attributes)] // incorrect lint; this doc(hidden) attr *is* respected by rustdoc #[doc(hidden)] type Id = crate::type_id!($cxxpath); type Kind = $kind; @@ -212,6 +215,9 @@ impl_extern_type! { isize = "rust::isize" f32 = "float" f64 = "double" + + #[cfg(feature = "alloc")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] String = "rust::String" [Opaque] @@ -1,4 +1,4 @@ -use std::fmt::{self, Display}; +use core::fmt::{self, Display}; pub(crate) fn display(fmt: impl Fn(&mut fmt::Formatter) -> fmt::Result) -> impl Display { DisplayInvoke(fmt) diff --git a/src/hash.rs b/src/hash.rs new file mode 100644 index 00000000..4c92173f --- /dev/null +++ b/src/hash.rs @@ -0,0 +1,12 @@ +use core::hash::{Hash, Hasher}; + +#[doc(hidden)] +pub fn hash<V: Hash>(value: &V) -> usize { + #[cfg(feature = "std")] + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + #[cfg(not(feature = "std"))] + let mut hasher = crate::sip::SipHasher13::new(); + + Hash::hash(value, &mut hasher); + Hasher::finish(&hasher) as usize +} @@ -2,7 +2,7 @@ //! //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust -//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs //! //! <br> //! @@ -364,10 +364,11 @@ //! </table> #![no_std] -#![doc(html_root_url = "https://docs.rs/cxx/1.0.54")] +#![doc(html_root_url = "https://docs.rs/cxx/1.0.85")] #![deny(improper_ctypes, improper_ctypes_definitions, missing_docs)] #![cfg_attr(not(no_unsafe_op_in_unsafe_fn_lint), deny(unsafe_op_in_unsafe_fn))] #![cfg_attr(no_unsafe_op_in_unsafe_fn_lint, allow(unused_unsafe))] +#![cfg_attr(doc_cfg, feature(doc_cfg))] #![allow(non_camel_case_types)] #![allow( clippy::cognitive_complexity, @@ -388,6 +389,7 @@ clippy::or_fun_call, clippy::ptr_arg, clippy::toplevel_ref_arg, + clippy::transmute_undefined_repr, // clippy bug: https://github.com/rust-lang/rust-clippy/issues/8417 clippy::useless_let_if_seq, clippy::wrong_self_convention )] @@ -395,18 +397,53 @@ #[cfg(built_with_cargo)] extern crate link_cplusplus; -extern crate alloc; extern crate self as cxx; -extern crate std; + +#[doc(hidden)] +pub extern crate core; + +#[cfg(feature = "alloc")] +#[doc(hidden)] +pub extern crate alloc; + +#[cfg(not(feature = "alloc"))] +extern crate core as alloc; + +#[cfg(feature = "std")] +#[doc(hidden)] +pub extern crate std; + +// Block inadvertent use of items from libstd, which does not otherwise produce +// a compile-time error on edition 2018+. +#[cfg(not(feature = "std"))] +extern crate core as std; + +#[cfg(not(any(feature = "alloc", cxx_experimental_no_alloc)))] +compile_error! { + r#"cxx support for no_alloc is incomplete and semver exempt; you must build with at least one of feature="std", feature="alloc", or RUSTFLAGS='--cfg cxx_experimental_no_alloc'"# +} + +#[cfg(all(compile_error_if_alloc, feature = "alloc"))] +compile_error! { + r#"feature="alloc" is unexpectedly enabled"# +} + +#[cfg(all(compile_error_if_std, feature = "std"))] +compile_error! { + r#"feature="std" is unexpectedly enabled"# +} #[macro_use] mod macros; +mod c_char; mod cxx_vector; mod exception; mod extern_type; mod fmt; mod function; +mod hash; +mod lossy; pub mod memory; mod opaque; mod result; @@ -416,6 +453,7 @@ mod rust_string; mod rust_type; mod rust_vec; mod shared_ptr; +mod sip; #[path = "cxx_string.rs"] mod string; mod symbols; @@ -426,6 +464,7 @@ pub mod vector; mod weak_ptr; pub use crate::cxx_vector::CxxVector; +#[cfg(feature = "alloc")] pub use crate::exception::Exception; pub use crate::extern_type::{kind, ExternType}; pub use crate::shared_ptr::SharedPtr; @@ -451,21 +490,27 @@ pub type Vector<T> = CxxVector<T>; // Not public API. #[doc(hidden)] pub mod private { + pub use crate::c_char::c_char; pub use crate::cxx_vector::VectorElement; pub use crate::extern_type::{verify_extern_kind, verify_extern_type}; pub use crate::function::FatFunction; + pub use crate::hash::hash; pub use crate::opaque::Opaque; + #[cfg(feature = "alloc")] pub use crate::result::{r#try, Result}; pub use crate::rust_slice::RustSlice; pub use crate::rust_str::RustStr; + #[cfg(feature = "alloc")] pub use crate::rust_string::RustString; pub use crate::rust_type::{ImplBox, ImplVec, RustType}; + #[cfg(feature = "alloc")] pub use crate::rust_vec::RustVec; pub use crate::shared_ptr::SharedPtrTarget; pub use crate::string::StackString; pub use crate::unique_ptr::UniquePtrTarget; - pub use crate::unwind::catch_unwind; + pub use crate::unwind::prevent_unwind; pub use crate::weak_ptr::WeakPtrTarget; + pub use core::{concat, module_path}; pub use cxxbridge_macro::type_id; } diff --git a/src/lossy.rs b/src/lossy.rs new file mode 100644 index 00000000..d68a2e7b --- /dev/null +++ b/src/lossy.rs @@ -0,0 +1,67 @@ +use core::char; +use core::fmt::{self, Write as _}; +use core::str; + +pub fn display(mut bytes: &[u8], f: &mut fmt::Formatter) -> fmt::Result { + loop { + match str::from_utf8(bytes) { + Ok(valid) => return f.write_str(valid), + Err(utf8_error) => { + let valid_up_to = utf8_error.valid_up_to(); + let valid = unsafe { str::from_utf8_unchecked(&bytes[..valid_up_to]) }; + f.write_str(valid)?; + f.write_char(char::REPLACEMENT_CHARACTER)?; + if let Some(error_len) = utf8_error.error_len() { + bytes = &bytes[valid_up_to + error_len..]; + } else { + return Ok(()); + } + } + } + } +} + +pub fn debug(mut bytes: &[u8], f: &mut fmt::Formatter) -> fmt::Result { + f.write_char('"')?; + + while !bytes.is_empty() { + let from_utf8_result = str::from_utf8(bytes); + let valid = match from_utf8_result { + Ok(valid) => valid, + Err(utf8_error) => { + let valid_up_to = utf8_error.valid_up_to(); + unsafe { str::from_utf8_unchecked(&bytes[..valid_up_to]) } + } + }; + + let mut written = 0; + for (i, ch) in valid.char_indices() { + let esc = ch.escape_debug(); + if esc.len() != 1 { + f.write_str(&valid[written..i])?; + for ch in esc { + f.write_char(ch)?; + } + written = i + ch.len_utf8(); + } + } + f.write_str(&valid[written..])?; + + match from_utf8_result { + Ok(_valid) => break, + Err(utf8_error) => { + let end_of_broken = if let Some(error_len) = utf8_error.error_len() { + valid.len() + error_len + } else { + bytes.len() + }; + for b in &bytes[valid.len()..end_of_broken] { + write!(f, "\\x{:02x}", b)?; + } + bytes = &bytes[end_of_broken..]; + } + } + } + + f.write_char('"') +} diff --git a/src/result.rs b/src/result.rs index d7a31f02..ba77858e 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "alloc")] #![allow(missing_docs)] use crate::exception::Exception; @@ -11,9 +12,9 @@ use core::str; #[repr(C)] #[derive(Copy, Clone)] -struct PtrLen { - ptr: NonNull<u8>, - len: usize, +pub struct PtrLen { + pub ptr: NonNull<u8>, + pub len: usize, } #[repr(C)] diff --git a/src/rust_string.rs b/src/rust_string.rs index 051e35c3..0e0c5a83 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "alloc")] #![allow(missing_docs)] use alloc::string::String; diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 9f4484db..acb7e890 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "alloc")] #![allow(missing_docs)] use crate::rust_string::RustString; @@ -67,6 +68,10 @@ impl<T> RustVec<T> { pub unsafe fn set_len(&mut self, len: usize) { unsafe { self.as_mut_vec().set_len(len) } } + + pub fn truncate(&mut self, len: usize) { + self.as_mut_vec().truncate(len); + } } impl RustVec<RustString> { diff --git a/src/shared_ptr.rs b/src/shared_ptr.rs index 317773d4..64c86619 100644 --- a/src/shared_ptr.rs +++ b/src/shared_ptr.rs @@ -101,6 +101,10 @@ where } } +// SharedPtr is not a self-referential type and is safe to move out of a Pin, +// regardless whether the pointer's target is Unpin. +impl<T> Unpin for SharedPtr<T> where T: SharedPtrTarget {} + impl<T> Drop for SharedPtr<T> where T: SharedPtrTarget, @@ -204,11 +208,9 @@ pub unsafe trait SharedPtrTarget { macro_rules! impl_shared_ptr_target { ($segment:expr, $name:expr, $ty:ty) => { unsafe impl SharedPtrTarget for $ty { - #[doc(hidden)] fn __typename(f: &mut fmt::Formatter) -> fmt::Result { f.write_str($name) } - #[doc(hidden)] unsafe fn __null(new: *mut c_void) { extern "C" { attr! { @@ -218,7 +220,6 @@ macro_rules! impl_shared_ptr_target { } unsafe { __null(new) } } - #[doc(hidden)] unsafe fn __new(value: Self, new: *mut c_void) { extern "C" { attr! { @@ -228,7 +229,6 @@ macro_rules! impl_shared_ptr_target { } unsafe { __uninit(new).cast::<$ty>().write(value) } } - #[doc(hidden)] unsafe fn __clone(this: *const c_void, new: *mut c_void) { extern "C" { attr! { @@ -238,7 +238,6 @@ macro_rules! impl_shared_ptr_target { } unsafe { __clone(this, new) } } - #[doc(hidden)] unsafe fn __get(this: *const c_void) -> *const Self { extern "C" { attr! { @@ -248,7 +247,6 @@ macro_rules! impl_shared_ptr_target { } unsafe { __get(this) }.cast() } - #[doc(hidden)] unsafe fn __drop(this: *mut c_void) { extern "C" { attr! { diff --git a/src/sip.rs b/src/sip.rs new file mode 100644 index 00000000..9e1d050a --- /dev/null +++ b/src/sip.rs @@ -0,0 +1,228 @@ +// Vendored from libstd: +// https://github.com/rust-lang/rust/blob/1.57.0/library/core/src/hash/sip.rs +// +// TODO: maybe depend on a hasher from crates.io if this becomes annoying to +// maintain, or change this to a simpler one. + +#![cfg(not(feature = "std"))] + +use core::cmp; +use core::hash::Hasher; +use core::mem; +use core::ptr; + +/// An implementation of SipHash 1-3. +/// +/// This is currently the default hashing function used by standard library +/// (e.g., `collections::HashMap` uses it by default). +/// +/// See: <https://131002.net/siphash> +pub struct SipHasher13 { + k0: u64, + k1: u64, + length: usize, // how many bytes we've processed + state: State, // hash State + tail: u64, // unprocessed bytes le + ntail: usize, // how many bytes in tail are valid +} + +#[derive(Clone, Copy)] +#[repr(C)] +struct State { + // v0, v2 and v1, v3 show up in pairs in the algorithm, + // and simd implementations of SipHash will use vectors + // of v02 and v13. By placing them in this order in the struct, + // the compiler can pick up on just a few simd optimizations by itself. + v0: u64, + v2: u64, + v1: u64, + v3: u64, +} + +macro_rules! compress { + ($state:expr) => { + compress!($state.v0, $state.v1, $state.v2, $state.v3) + }; + ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => { + $v0 = $v0.wrapping_add($v1); + $v1 = $v1.rotate_left(13); + $v1 ^= $v0; + $v0 = $v0.rotate_left(32); + $v2 = $v2.wrapping_add($v3); + $v3 = $v3.rotate_left(16); + $v3 ^= $v2; + $v0 = $v0.wrapping_add($v3); + $v3 = $v3.rotate_left(21); + $v3 ^= $v0; + $v2 = $v2.wrapping_add($v1); + $v1 = $v1.rotate_left(17); + $v1 ^= $v2; + $v2 = $v2.rotate_left(32); + }; +} + +/// Loads an integer of the desired type from a byte stream, in LE order. Uses +/// `copy_nonoverlapping` to let the compiler generate the most efficient way +/// to load it from a possibly unaligned address. +/// +/// Unsafe because: unchecked indexing at i..i+size_of(int_ty) +macro_rules! load_int_le { + ($buf:expr, $i:expr, $int_ty:ident) => {{ + debug_assert!($i + mem::size_of::<$int_ty>() <= $buf.len()); + let mut data = 0 as $int_ty; + ptr::copy_nonoverlapping( + $buf.as_ptr().add($i), + &mut data as *mut _ as *mut u8, + mem::size_of::<$int_ty>(), + ); + data.to_le() + }}; +} + +/// Loads a u64 using up to 7 bytes of a byte slice. It looks clumsy but the +/// `copy_nonoverlapping` calls that occur (via `load_int_le!`) all have fixed +/// sizes and avoid calling `memcpy`, which is good for speed. +/// +/// Unsafe because: unchecked indexing at start..start+len +unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 { + debug_assert!(len < 8); + let mut i = 0; // current byte index (from LSB) in the output u64 + let mut out = 0; + if i + 3 < len { + // SAFETY: `i` cannot be greater than `len`, and the caller must guarantee + // that the index start..start+len is in bounds. + out = unsafe { load_int_le!(buf, start + i, u32) } as u64; + i += 4; + } + if i + 1 < len { + // SAFETY: same as above. + out |= (unsafe { load_int_le!(buf, start + i, u16) } as u64) << (i * 8); + i += 2 + } + if i < len { + // SAFETY: same as above. + out |= (unsafe { *buf.get_unchecked(start + i) } as u64) << (i * 8); + i += 1; + } + debug_assert_eq!(i, len); + out +} + +impl SipHasher13 { + /// Creates a new `SipHasher13` with the two initial keys set to 0. + pub fn new() -> Self { + Self::new_with_keys(0, 0) + } + + /// Creates a `SipHasher13` that is keyed off the provided keys. + fn new_with_keys(key0: u64, key1: u64) -> Self { + let mut state = SipHasher13 { + k0: key0, + k1: key1, + length: 0, + state: State { + v0: 0, + v1: 0, + v2: 0, + v3: 0, + }, + tail: 0, + ntail: 0, + }; + state.reset(); + state + } + + fn reset(&mut self) { + self.length = 0; + self.state.v0 = self.k0 ^ 0x736f6d6570736575; + self.state.v1 = self.k1 ^ 0x646f72616e646f6d; + self.state.v2 = self.k0 ^ 0x6c7967656e657261; + self.state.v3 = self.k1 ^ 0x7465646279746573; + self.ntail = 0; + } +} + +impl Hasher for SipHasher13 { + // Note: no integer hashing methods (`write_u*`, `write_i*`) are defined + // for this type. We could add them, copy the `short_write` implementation + // in librustc_data_structures/sip128.rs, and add `write_u*`/`write_i*` + // methods to `SipHasher`, `SipHasher13`, and `DefaultHasher`. This would + // greatly speed up integer hashing by those hashers, at the cost of + // slightly slowing down compile speeds on some benchmarks. See #69152 for + // details. + fn write(&mut self, msg: &[u8]) { + let length = msg.len(); + self.length += length; + + let mut needed = 0; + + if self.ntail != 0 { + needed = 8 - self.ntail; + // SAFETY: `cmp::min(length, needed)` is guaranteed to not be over `length` + self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << (8 * self.ntail); + if length < needed { + self.ntail += length; + return; + } else { + self.state.v3 ^= self.tail; + Sip13Rounds::c_rounds(&mut self.state); + self.state.v0 ^= self.tail; + self.ntail = 0; + } + } + + // Buffered tail is now flushed, process new input. + let len = length - needed; + let left = len & 0x7; // len % 8 + + let mut i = needed; + while i < len - left { + // SAFETY: because `len - left` is the biggest multiple of 8 under + // `len`, and because `i` starts at `needed` where `len` is `length - needed`, + // `i + 8` is guaranteed to be less than or equal to `length`. + let mi = unsafe { load_int_le!(msg, i, u64) }; + + self.state.v3 ^= mi; + Sip13Rounds::c_rounds(&mut self.state); + self.state.v0 ^= mi; + + i += 8; + } + + // SAFETY: `i` is now `needed + len.div_euclid(8) * 8`, + // so `i + left` = `needed + len` = `length`, which is by + // definition equal to `msg.len()`. + self.tail = unsafe { u8to64_le(msg, i, left) }; + self.ntail = left; + } + + fn finish(&self) -> u64 { + let mut state = self.state; + + let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail; + + state.v3 ^= b; + Sip13Rounds::c_rounds(&mut state); + state.v0 ^= b; + + state.v2 ^= 0xff; + Sip13Rounds::d_rounds(&mut state); + + state.v0 ^ state.v1 ^ state.v2 ^ state.v3 + } +} + +struct Sip13Rounds; + +impl Sip13Rounds { + fn c_rounds(state: &mut State) { + compress!(state); + } + + fn d_rounds(state: &mut State) { + compress!(state); + compress!(state); + compress!(state); + } +} diff --git a/src/symbols/exception.rs b/src/symbols/exception.rs index cf0701ba..b8fe1b5d 100644 --- a/src/symbols/exception.rs +++ b/src/symbols/exception.rs @@ -1,10 +1,18 @@ +#![cfg(feature = "alloc")] + +use crate::result::PtrLen; use alloc::boxed::Box; use alloc::string::String; +use core::ptr::NonNull; use core::slice; #[export_name = "cxxbridge1$exception"] -unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 { +unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> PtrLen { let slice = unsafe { slice::from_raw_parts(ptr, len) }; - let boxed = String::from_utf8_lossy(slice).into_owned().into_boxed_str(); - Box::leak(boxed).as_ptr() + let string = String::from_utf8_lossy(slice); + let len = string.len(); + let raw_str = Box::into_raw(string.into_owned().into_boxed_str()); + let raw_u8 = raw_str.cast::<u8>(); + let nonnull = unsafe { NonNull::new_unchecked(raw_u8) }; + PtrLen { ptr: nonnull, len } } diff --git a/src/symbols/rust_str.rs b/src/symbols/rust_str.rs index 3d5ec344..3b33bc4a 100644 --- a/src/symbols/rust_str.rs +++ b/src/symbols/rust_str.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "alloc")] use alloc::string::String; use core::mem::MaybeUninit; use core::ptr; @@ -10,6 +11,7 @@ unsafe extern "C" fn str_new(this: &mut MaybeUninit<&str>) { unsafe { ptr::write(this, "") } } +#[cfg(feature = "alloc")] #[export_name = "cxxbridge1$str$ref"] unsafe extern "C" fn str_ref<'a>(this: &mut MaybeUninit<&'a str>, string: &'a String) { let this = this.as_mut_ptr(); diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs index 49d40697..8b7c8c48 100644 --- a/src/symbols/rust_string.rs +++ b/src/symbols/rust_string.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "alloc")] + use alloc::borrow::ToOwned; use alloc::string::String; use core::mem::{ManuallyDrop, MaybeUninit}; @@ -37,6 +39,18 @@ unsafe extern "C" fn string_from_utf8( } } +#[export_name = "cxxbridge1$string$from_utf8_lossy"] +unsafe extern "C" fn string_from_utf8_lossy( + this: &mut MaybeUninit<String>, + ptr: *const u8, + len: usize, +) { + let slice = unsafe { slice::from_raw_parts(ptr, len) }; + let owned = String::from_utf8_lossy(slice).into_owned(); + let this = this.as_mut_ptr(); + unsafe { ptr::write(this, owned) } +} + #[export_name = "cxxbridge1$string$from_utf16"] unsafe extern "C" fn string_from_utf16( this: &mut MaybeUninit<String>, @@ -54,6 +68,18 @@ unsafe extern "C" fn string_from_utf16( } } +#[export_name = "cxxbridge1$string$from_utf16_lossy"] +unsafe extern "C" fn string_from_utf16_lossy( + this: &mut MaybeUninit<String>, + ptr: *const u16, + len: usize, +) { + let slice = unsafe { slice::from_raw_parts(ptr, len) }; + let owned = String::from_utf16_lossy(slice); + let this = this.as_mut_ptr(); + unsafe { ptr::write(this, owned) } +} + #[export_name = "cxxbridge1$string$drop"] unsafe extern "C" fn string_drop(this: &mut ManuallyDrop<String>) { unsafe { ManuallyDrop::drop(this) } diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index 6f2dab9d..89c7da44 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -1,9 +1,11 @@ +#![cfg(feature = "alloc")] + +use crate::c_char::c_char; use crate::rust_string::RustString; use crate::rust_vec::RustVec; use alloc::vec::Vec; use core::mem; use core::ptr; -use std::os::raw::c_char; macro_rules! rust_vec_shims { ($segment:expr, $ty:ty) => { @@ -54,6 +56,12 @@ macro_rules! rust_vec_shims { unsafe { (*this).set_len(len) } } } + attr! { + #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$truncate")] + unsafe extern "C" fn __truncate(this: *mut RustVec<$ty>, len: usize) { + unsafe { (*this).truncate(len) } + } + } }; }; } diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 63a1ca78..33992059 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -112,6 +112,10 @@ where unsafe impl<T> Send for UniquePtr<T> where T: Send + UniquePtrTarget {} unsafe impl<T> Sync for UniquePtr<T> where T: Sync + UniquePtrTarget {} +// UniquePtr is not a self-referential type and is safe to move out of a Pin, +// regardless whether the pointer's target is Unpin. +impl<T> Unpin for UniquePtr<T> where T: UniquePtrTarget {} + impl<T> Drop for UniquePtr<T> where T: UniquePtrTarget, @@ -241,11 +245,9 @@ extern "C" { } unsafe impl UniquePtrTarget for CxxString { - #[doc(hidden)] fn __typename(f: &mut fmt::Formatter) -> fmt::Result { f.write_str("CxxString") } - #[doc(hidden)] fn __null() -> MaybeUninit<*mut c_void> { let mut repr = MaybeUninit::uninit(); unsafe { @@ -253,21 +255,17 @@ unsafe impl UniquePtrTarget for CxxString { } repr } - #[doc(hidden)] unsafe fn __raw(raw: *mut Self) -> MaybeUninit<*mut c_void> { let mut repr = MaybeUninit::uninit(); unsafe { unique_ptr_std_string_raw(&mut repr, raw) } repr } - #[doc(hidden)] unsafe fn __get(repr: MaybeUninit<*mut c_void>) -> *const Self { unsafe { unique_ptr_std_string_get(&repr) } } - #[doc(hidden)] unsafe fn __release(mut repr: MaybeUninit<*mut c_void>) -> *mut Self { unsafe { unique_ptr_std_string_release(&mut repr) } } - #[doc(hidden)] unsafe fn __drop(mut repr: MaybeUninit<*mut c_void>) { unsafe { unique_ptr_std_string_drop(&mut repr) } } @@ -277,27 +275,21 @@ unsafe impl<T> UniquePtrTarget for CxxVector<T> where T: VectorElement, { - #[doc(hidden)] fn __typename(f: &mut fmt::Formatter) -> fmt::Result { write!(f, "CxxVector<{}>", display(T::__typename)) } - #[doc(hidden)] fn __null() -> MaybeUninit<*mut c_void> { T::__unique_ptr_null() } - #[doc(hidden)] unsafe fn __raw(raw: *mut Self) -> MaybeUninit<*mut c_void> { unsafe { T::__unique_ptr_raw(raw) } } - #[doc(hidden)] unsafe fn __get(repr: MaybeUninit<*mut c_void>) -> *const Self { unsafe { T::__unique_ptr_get(repr) } } - #[doc(hidden)] unsafe fn __release(repr: MaybeUninit<*mut c_void>) -> *mut Self { unsafe { T::__unique_ptr_release(repr) } } - #[doc(hidden)] unsafe fn __drop(repr: MaybeUninit<*mut c_void>) { unsafe { T::__unique_ptr_drop(repr) } } diff --git a/src/unwind.rs b/src/unwind.rs index 4967a219..18852da7 100644 --- a/src/unwind.rs +++ b/src/unwind.rs @@ -1,26 +1,39 @@ #![allow(missing_docs)] -use std::io::{self, Write}; -use std::panic::{self, AssertUnwindSafe}; -use std::process; +use core::mem; -pub fn catch_unwind<F, R>(label: &'static str, foreign_call: F) -> R +pub fn prevent_unwind<F, R>(label: &'static str, foreign_call: F) -> R where F: FnOnce() -> R, { - // Regarding the AssertUnwindSafe: we immediately abort on panic so it - // doesn't matter whether the types involved are unwind-safe. The UnwindSafe - // bound on catch_unwind is about ensuring nothing is in a broken state if - // your program plans to continue after the panic. - match panic::catch_unwind(AssertUnwindSafe(foreign_call)) { - Ok(ret) => ret, - Err(_) => abort(label), - } + // Goal is to make it impossible to propagate a panic across the C interface + // of an extern "Rust" function, which would be Undefined Behavior. We + // transform such panicks into a deterministic abort instead. When cxx is + // built in an application using panic=abort, this guard object is compiled + // out because its destructor is statically unreachable. When built with + // panic=unwind, an unwind from the foreign call will attempt to drop the + // guard object leading to a double panic, which is defined by Rust to + // abort. In no_std programs, on most platforms the current mechanism for + // this is for core::intrinsics::abort to invoke an invalid instruction. On + // Unix, the process will probably terminate with a signal like SIGABRT, + // SIGILL, SIGTRAP, SIGSEGV or SIGBUS. The precise behaviour is not + // guaranteed and not stable, but is safe. + let guard = Guard { label }; + + let ret = foreign_call(); + + // If we made it here, no uncaught panic occurred during the foreign call. + mem::forget(guard); + ret } -#[cold] -fn abort(label: &'static str) -> ! { - let mut stderr = io::stderr(); - let _ = writeln!(stderr, "Error: panic in ffi function {}, aborting.", label); - process::abort(); +struct Guard { + label: &'static str, +} + +impl Drop for Guard { + #[cold] + fn drop(&mut self) { + panic!("panic in ffi function {}, aborting.", self.label); + } } diff --git a/src/weak_ptr.rs b/src/weak_ptr.rs index 8a9f1a68..e9320f37 100644 --- a/src/weak_ptr.rs +++ b/src/weak_ptr.rs @@ -114,11 +114,9 @@ pub unsafe trait WeakPtrTarget { macro_rules! impl_weak_ptr_target { ($segment:expr, $name:expr, $ty:ty) => { unsafe impl WeakPtrTarget for $ty { - #[doc(hidden)] fn __typename(f: &mut fmt::Formatter) -> fmt::Result { f.write_str($name) } - #[doc(hidden)] unsafe fn __null(new: *mut c_void) { extern "C" { attr! { @@ -128,7 +126,6 @@ macro_rules! impl_weak_ptr_target { } unsafe { __null(new) } } - #[doc(hidden)] unsafe fn __clone(this: *const c_void, new: *mut c_void) { extern "C" { attr! { @@ -138,7 +135,6 @@ macro_rules! impl_weak_ptr_target { } unsafe { __clone(this, new) } } - #[doc(hidden)] unsafe fn __downgrade(shared: *const c_void, weak: *mut c_void) { extern "C" { attr! { @@ -148,7 +144,6 @@ macro_rules! impl_weak_ptr_target { } unsafe { __downgrade(shared, weak) } } - #[doc(hidden)] unsafe fn __upgrade(weak: *const c_void, shared: *mut c_void) { extern "C" { attr! { @@ -158,7 +153,6 @@ macro_rules! impl_weak_ptr_target { } unsafe { __upgrade(weak, shared) } } - #[doc(hidden)] unsafe fn __drop(this: *mut c_void) { extern "C" { attr! { |