• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::actually_private::Private;
2 use crate::lossy;
3 #[cfg(feature = "alloc")]
4 use alloc::borrow::Cow;
5 #[cfg(feature = "alloc")]
6 use alloc::string::String;
7 use core::cmp::Ordering;
8 use core::fmt::{self, Debug, Display};
9 use core::hash::{Hash, Hasher};
10 use core::marker::{PhantomData, PhantomPinned};
11 use core::mem::MaybeUninit;
12 use core::pin::Pin;
13 use core::slice;
14 use core::str::{self, Utf8Error};
15 
16 extern "C" {
17     #[link_name = "cxxbridge1$cxx_string$init"]
string_init(this: &mut MaybeUninit<CxxString>, ptr: *const u8, len: usize)18     fn string_init(this: &mut MaybeUninit<CxxString>, ptr: *const u8, len: usize);
19     #[link_name = "cxxbridge1$cxx_string$destroy"]
string_destroy(this: &mut MaybeUninit<CxxString>)20     fn string_destroy(this: &mut MaybeUninit<CxxString>);
21     #[link_name = "cxxbridge1$cxx_string$data"]
string_data(this: &CxxString) -> *const u822     fn string_data(this: &CxxString) -> *const u8;
23     #[link_name = "cxxbridge1$cxx_string$length"]
string_length(this: &CxxString) -> usize24     fn string_length(this: &CxxString) -> usize;
25     #[link_name = "cxxbridge1$cxx_string$clear"]
string_clear(this: Pin<&mut CxxString>)26     fn string_clear(this: Pin<&mut CxxString>);
27     #[link_name = "cxxbridge1$cxx_string$reserve_total"]
string_reserve_total(this: Pin<&mut CxxString>, new_cap: usize)28     fn string_reserve_total(this: Pin<&mut CxxString>, new_cap: usize);
29     #[link_name = "cxxbridge1$cxx_string$push"]
string_push(this: Pin<&mut CxxString>, ptr: *const u8, len: usize)30     fn string_push(this: Pin<&mut CxxString>, ptr: *const u8, len: usize);
31 }
32 
33 /// Binding to C++ `std::string`.
34 ///
35 /// # Invariants
36 ///
37 /// As an invariant of this API and the static analysis of the cxx::bridge
38 /// macro, in Rust code we can never obtain a `CxxString` by value. C++'s string
39 /// requires a move constructor and may hold internal pointers, which is not
40 /// compatible with Rust's move behavior. Instead in Rust code we will only ever
41 /// look at a CxxString through a reference or smart pointer, as in `&CxxString`
42 /// or `UniquePtr<CxxString>`.
43 #[repr(C)]
44 pub struct CxxString {
45     _private: [u8; 0],
46     _pinned: PhantomData<PhantomPinned>,
47 }
48 
49 /// Construct a C++ std::string on the Rust stack.
50 ///
51 /// # Syntax
52 ///
53 /// In statement position:
54 ///
55 /// ```
56 /// # use cxx::let_cxx_string;
57 /// # let expression = "";
58 /// let_cxx_string!(var = expression);
59 /// ```
60 ///
61 /// The `expression` may have any type that implements `AsRef<[u8]>`. Commonly
62 /// it will be a string literal, but for example `&[u8]` and `String` would work
63 /// as well.
64 ///
65 /// The macro expands to something resembling `let $var: Pin<&mut CxxString> =
66 /// /*???*/;`. The resulting [`Pin`] can be deref'd to `&CxxString` as needed.
67 ///
68 /// # Example
69 ///
70 /// ```
71 /// use cxx::{let_cxx_string, CxxString};
72 ///
73 /// fn f(s: &CxxString) {/* ... */}
74 ///
75 /// fn main() {
76 ///     let_cxx_string!(s = "example");
77 ///     f(&s);
78 /// }
79 /// ```
80 #[macro_export]
81 macro_rules! let_cxx_string {
82     ($var:ident = $value:expr $(,)?) => {
83         let mut cxx_stack_string = $crate::private::StackString::new();
84         #[allow(unused_mut, unused_unsafe)]
85         let mut $var = match $value {
86             let_cxx_string => unsafe { cxx_stack_string.init(let_cxx_string) },
87         };
88     };
89 }
90 
91 impl CxxString {
92     /// `CxxString` is not constructible via `new`. Instead, use the
93     /// [`let_cxx_string!`] macro.
new<T: Private>() -> Self94     pub fn new<T: Private>() -> Self {
95         unreachable!()
96     }
97 
98     /// Returns the length of the string in bytes.
99     ///
100     /// Matches the behavior of C++ [std::string::size][size].
101     ///
102     /// [size]: https://en.cppreference.com/w/cpp/string/basic_string/size
len(&self) -> usize103     pub fn len(&self) -> usize {
104         unsafe { string_length(self) }
105     }
106 
107     /// Returns true if `self` has a length of zero bytes.
108     ///
109     /// Matches the behavior of C++ [std::string::empty][empty].
110     ///
111     /// [empty]: https://en.cppreference.com/w/cpp/string/basic_string/empty
is_empty(&self) -> bool112     pub fn is_empty(&self) -> bool {
113         self.len() == 0
114     }
115 
116     /// Returns a byte slice of this string's contents.
as_bytes(&self) -> &[u8]117     pub fn as_bytes(&self) -> &[u8] {
118         let data = self.as_ptr();
119         let len = self.len();
120         unsafe { slice::from_raw_parts(data, len) }
121     }
122 
123     /// Produces a pointer to the first character of the string.
124     ///
125     /// Matches the behavior of C++ [std::string::data][data].
126     ///
127     /// Note that the return type may look like `const char *` but is not a
128     /// `const char *` in the typical C sense, as C++ strings may contain
129     /// internal null bytes. As such, the returned pointer only makes sense as a
130     /// string in combination with the length returned by [`len()`][len].
131     ///
132     /// [data]: https://en.cppreference.com/w/cpp/string/basic_string/data
133     /// [len]: #method.len
as_ptr(&self) -> *const u8134     pub fn as_ptr(&self) -> *const u8 {
135         unsafe { string_data(self) }
136     }
137 
138     /// Validates that the C++ string contains UTF-8 data and produces a view of
139     /// it as a Rust &amp;str, otherwise an error.
to_str(&self) -> Result<&str, Utf8Error>140     pub fn to_str(&self) -> Result<&str, Utf8Error> {
141         str::from_utf8(self.as_bytes())
142     }
143 
144     /// If the contents of the C++ string are valid UTF-8, this function returns
145     /// a view as a Cow::Borrowed &amp;str. Otherwise replaces any invalid UTF-8
146     /// sequences with the U+FFFD [replacement character] and returns a
147     /// Cow::Owned String.
148     ///
149     /// [replacement character]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html
150     #[cfg(feature = "alloc")]
151     #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
to_string_lossy(&self) -> Cow<str>152     pub fn to_string_lossy(&self) -> Cow<str> {
153         String::from_utf8_lossy(self.as_bytes())
154     }
155 
156     /// Removes all characters from the string.
157     ///
158     /// Matches the behavior of C++ [std::string::clear][clear].
159     ///
160     /// Note: **unlike** the guarantee of Rust's `std::string::String::clear`,
161     /// the C++ standard does not require that capacity is unchanged by this
162     /// operation. In practice existing implementations do not change the
163     /// capacity but all pointers, references, and iterators into the string
164     /// contents are nevertheless invalidated.
165     ///
166     /// [clear]: https://en.cppreference.com/w/cpp/string/basic_string/clear
clear(self: Pin<&mut Self>)167     pub fn clear(self: Pin<&mut Self>) {
168         unsafe { string_clear(self) }
169     }
170 
171     /// Ensures that this string's capacity is at least `additional` bytes
172     /// larger than its length.
173     ///
174     /// The capacity may be increased by more than `additional` bytes if it
175     /// chooses, to amortize the cost of frequent reallocations.
176     ///
177     /// **The meaning of the argument is not the same as
178     /// [std::string::reserve][reserve] in C++.** The C++ standard library and
179     /// Rust standard library both have a `reserve` method on strings, but in
180     /// C++ code the argument always refers to total capacity, whereas in Rust
181     /// code it always refers to additional capacity. This API on `CxxString`
182     /// follows the Rust convention, the same way that for the length accessor
183     /// we use the Rust conventional `len()` naming and not C++ `size()` or
184     /// `length()`.
185     ///
186     /// # Panics
187     ///
188     /// Panics if the new capacity overflows usize.
189     ///
190     /// [reserve]: https://en.cppreference.com/w/cpp/string/basic_string/reserve
reserve(self: Pin<&mut Self>, additional: usize)191     pub fn reserve(self: Pin<&mut Self>, additional: usize) {
192         let new_cap = self
193             .len()
194             .checked_add(additional)
195             .expect("CxxString capacity overflow");
196         unsafe { string_reserve_total(self, new_cap) }
197     }
198 
199     /// Appends a given string slice onto the end of this C++ string.
push_str(self: Pin<&mut Self>, s: &str)200     pub fn push_str(self: Pin<&mut Self>, s: &str) {
201         self.push_bytes(s.as_bytes());
202     }
203 
204     /// Appends arbitrary bytes onto the end of this C++ string.
push_bytes(self: Pin<&mut Self>, bytes: &[u8])205     pub fn push_bytes(self: Pin<&mut Self>, bytes: &[u8]) {
206         unsafe { string_push(self, bytes.as_ptr(), bytes.len()) }
207     }
208 }
209 
210 impl Display for CxxString {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result211     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212         lossy::display(self.as_bytes(), f)
213     }
214 }
215 
216 impl Debug for CxxString {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result217     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218         lossy::debug(self.as_bytes(), f)
219     }
220 }
221 
222 impl PartialEq for CxxString {
eq(&self, other: &Self) -> bool223     fn eq(&self, other: &Self) -> bool {
224         self.as_bytes() == other.as_bytes()
225     }
226 }
227 
228 impl PartialEq<CxxString> for str {
eq(&self, other: &CxxString) -> bool229     fn eq(&self, other: &CxxString) -> bool {
230         self.as_bytes() == other.as_bytes()
231     }
232 }
233 
234 impl PartialEq<str> for CxxString {
eq(&self, other: &str) -> bool235     fn eq(&self, other: &str) -> bool {
236         self.as_bytes() == other.as_bytes()
237     }
238 }
239 
240 impl Eq for CxxString {}
241 
242 impl PartialOrd for CxxString {
partial_cmp(&self, other: &Self) -> Option<Ordering>243     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
244         self.as_bytes().partial_cmp(other.as_bytes())
245     }
246 }
247 
248 impl Ord for CxxString {
cmp(&self, other: &Self) -> Ordering249     fn cmp(&self, other: &Self) -> Ordering {
250         self.as_bytes().cmp(other.as_bytes())
251     }
252 }
253 
254 impl Hash for CxxString {
hash<H: Hasher>(&self, state: &mut H)255     fn hash<H: Hasher>(&self, state: &mut H) {
256         self.as_bytes().hash(state);
257     }
258 }
259 
260 #[doc(hidden)]
261 #[repr(C)]
262 pub struct StackString {
263     // Static assertions in cxx.cc validate that this is large enough and
264     // aligned enough.
265     space: MaybeUninit<[usize; 8]>,
266 }
267 
268 #[allow(missing_docs)]
269 impl StackString {
new() -> Self270     pub fn new() -> Self {
271         StackString {
272             space: MaybeUninit::uninit(),
273         }
274     }
275 
init(&mut self, value: impl AsRef<[u8]>) -> Pin<&mut CxxString>276     pub unsafe fn init(&mut self, value: impl AsRef<[u8]>) -> Pin<&mut CxxString> {
277         let value = value.as_ref();
278         unsafe {
279             let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
280             string_init(this, value.as_ptr(), value.len());
281             Pin::new_unchecked(&mut *this.as_mut_ptr())
282         }
283     }
284 }
285 
286 impl Drop for StackString {
drop(&mut self)287     fn drop(&mut self) {
288         unsafe {
289             let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
290             string_destroy(this);
291         }
292     }
293 }
294