1use crate::actually_private::Private;
2use crate::lossy;
3#[cfg(feature = "alloc")]
4use alloc::borrow::Cow;
5#[cfg(feature = "alloc")]
6use alloc::string::String;
7use core::cell::UnsafeCell;
8use core::cmp::Ordering;
9use core::ffi::{CStr, c_char};
10use core::fmt::{self, Debug, Display};
11use core::hash::{Hash, Hasher};
12use core::marker::{PhantomData, PhantomPinned};
13use core::mem::MaybeUninit;
14use core::panic::RefUnwindSafe;
15use core::pin::Pin;
16use core::slice;
17use core::str::{self, Utf8Error};
18
19unsafe extern "C" {
20 #[link_name = "cxxbridge1$cxx_string$init"]
21 fn string_init(this: &mut MaybeUninit<CxxString>, ptr: *const u8, len: usize);
22 #[link_name = "cxxbridge1$cxx_string$destroy"]
23 fn string_destroy(this: &mut MaybeUninit<CxxString>);
24 #[link_name = "cxxbridge1$cxx_string$data"]
25 fn string_data(this: &CxxString) -> *const u8;
26 #[link_name = "cxxbridge1$cxx_string$length"]
27 fn string_length(this: &CxxString) -> usize;
28 #[link_name = "cxxbridge1$cxx_string$clear"]
29 fn string_clear(this: Pin<&mut CxxString>);
30 #[link_name = "cxxbridge1$cxx_string$reserve_total"]
31 fn string_reserve_total(this: Pin<&mut CxxString>, new_cap: usize);
32 #[link_name = "cxxbridge1$cxx_string$push"]
33 fn string_push(this: Pin<&mut CxxString>, ptr: *const u8, len: usize);
34}
35
36#[repr(C)]
47pub struct CxxString {
48 #[cfg(not(all(miri, feature = "alloc")))]
49 _private: [u8; 0],
50 #[cfg(all(miri, feature = "alloc"))]
51 _miri: miri::CxxStringRepr,
52 _pinned: PhantomData<PhantomPinned>,
53}
54
55#[macro_export]
87macro_rules! let_cxx_string {
88 ($var:ident = $value:expr $(,)?) => {
89 let cxx_stack_string = $crate::private::StackString::new();
90 #[allow(unused_mut, unused_unsafe)]
91 let mut $var = match $value {
92 let_cxx_string => unsafe { cxx_stack_string.init(let_cxx_string) },
93 };
94 #[allow(unused_unsafe)]
95 let _cxx_stack_string_drop_guard = unsafe { cxx_stack_string.drop_guard() };
96 };
97}
98
99impl CxxString {
100 pub fn new<T: Private>() -> Self {
103 unreachable!()
104 }
105
106 pub fn len(&self) -> usize {
112 unsafe { string_length(self) }
113 }
114
115 pub fn is_empty(&self) -> bool {
121 self.len() == 0
122 }
123
124 pub fn as_bytes(&self) -> &[u8] {
126 let data = self.as_ptr();
127 let len = self.len();
128 unsafe { slice::from_raw_parts(data, len) }
129 }
130
131 pub fn as_ptr(&self) -> *const u8 {
145 unsafe { string_data(self) }
146 }
147
148 pub fn as_c_str(&self) -> &CStr {
158 unsafe { CStr::from_ptr(self.as_ptr().cast::<c_char>()) }
160 }
161
162 pub fn to_str(&self) -> Result<&str, Utf8Error> {
165 str::from_utf8(self.as_bytes())
166 }
167
168 #[cfg(feature = "alloc")]
175 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
176 pub fn to_string_lossy(&self) -> Cow<str> {
177 String::from_utf8_lossy(self.as_bytes())
178 }
179
180 pub fn clear(self: Pin<&mut Self>) {
192 unsafe { string_clear(self) }
193 }
194
195 pub fn reserve(self: Pin<&mut Self>, additional: usize) {
216 let new_cap = self
217 .len()
218 .checked_add(additional)
219 .expect("CxxString capacity overflow");
220 unsafe { string_reserve_total(self, new_cap) }
221 }
222
223 pub fn push_str(self: Pin<&mut Self>, s: &str) {
225 self.push_bytes(s.as_bytes());
226 }
227
228 pub fn push_bytes(self: Pin<&mut Self>, bytes: &[u8]) {
230 unsafe { string_push(self, bytes.as_ptr(), bytes.len()) }
231 }
232}
233
234impl Display for CxxString {
235 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236 lossy::display(self.as_bytes(), f)
237 }
238}
239
240impl Debug for CxxString {
241 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
242 lossy::debug(self.as_bytes(), f)
243 }
244}
245
246impl PartialEq for CxxString {
247 fn eq(&self, other: &Self) -> bool {
248 self.as_bytes() == other.as_bytes()
249 }
250}
251
252impl PartialEq<CxxString> for str {
253 fn eq(&self, other: &CxxString) -> bool {
254 self.as_bytes() == other.as_bytes()
255 }
256}
257
258impl PartialEq<str> for CxxString {
259 fn eq(&self, other: &str) -> bool {
260 self.as_bytes() == other.as_bytes()
261 }
262}
263
264impl Eq for CxxString {}
265
266impl PartialOrd for CxxString {
267 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
268 Some(self.cmp(other))
269 }
270}
271
272impl Ord for CxxString {
273 fn cmp(&self, other: &Self) -> Ordering {
274 self.as_bytes().cmp(other.as_bytes())
275 }
276}
277
278impl Hash for CxxString {
279 fn hash<H: Hasher>(&self, state: &mut H) {
280 self.as_bytes().hash(state);
281 }
282}
283
284impl fmt::Write for Pin<&mut CxxString> {
285 fn write_str(&mut self, s: &str) -> fmt::Result {
286 self.as_mut().push_str(s);
287 Ok(())
288 }
289}
290
291#[cfg(feature = "std")]
292impl std::io::Write for Pin<&mut CxxString> {
293 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
294 self.as_mut().push_bytes(buf);
295 Ok(buf.len())
296 }
297
298 fn flush(&mut self) -> std::io::Result<()> {
299 Ok(())
300 }
301}
302
303#[doc(hidden)]
304#[repr(C)]
305pub struct StackString {
306 space: UnsafeCell<MaybeUninit<[usize; 8]>>,
309}
310
311unsafe impl Sync for StackString {}
312impl RefUnwindSafe for StackString {}
313
314impl StackString {
315 pub fn new() -> Self {
316 StackString {
317 space: UnsafeCell::new(MaybeUninit::uninit()),
318 }
319 }
320
321 #[allow(clippy::mut_from_ref)]
322 pub unsafe fn init(&self, value: impl AsRef<[u8]>) -> Pin<&mut CxxString> {
323 let value = value.as_ref();
324 unsafe {
325 let this = &mut *self.space.get().cast::<MaybeUninit<CxxString>>();
326 string_init(this, value.as_ptr(), value.len());
327 Pin::new_unchecked(&mut *this.as_mut_ptr())
328 }
329 }
330
331 pub unsafe fn drop_guard(&self) -> impl Drop + '_ {
332 struct StackStringDropGuard<'a>(&'a StackString);
333
334 impl<'a> Drop for StackStringDropGuard<'a> {
335 fn drop(&mut self) {
336 unsafe {
337 let this = &mut *self.0.space.get().cast::<MaybeUninit<CxxString>>();
338 string_destroy(this);
339 }
340 }
341 }
342
343 StackStringDropGuard(self)
344 }
345}
346
347#[cfg(all(miri, feature = "alloc"))]
348mod miri {
349 use super::CxxString;
350 use alloc::vec::Vec;
351 use core::mem;
352 use core::mem::MaybeUninit;
353 use core::pin::Pin;
354 use core::ptr;
355 use core::slice;
356
357 pub(super) type CxxStringRepr = [MaybeUninit<u8>; mem::size_of::<Vec<u8>>()];
358
359 #[unsafe(export_name = "cxxbridge1$cxx_string$init")]
360 unsafe extern "C" fn string_init(
361 this: &mut MaybeUninit<CxxString>,
362 ptr: *const u8,
363 len: usize,
364 ) {
365 unsafe {
366 this.as_mut_ptr()
367 .cast::<Vec<u8>>()
368 .write(slice::from_raw_parts(ptr, len).to_vec());
369 }
370 }
371
372 #[unsafe(export_name = "cxxbridge1$cxx_string$destroy")]
373 unsafe extern "C" fn string_destroy(this: &mut MaybeUninit<CxxString>) {
374 unsafe {
375 ptr::drop_in_place(this.as_mut_ptr().cast::<Vec<u8>>());
376 }
377 }
378
379 #[unsafe(export_name = "cxxbridge1$cxx_string$data")]
380 unsafe extern "C" fn string_data(this: &CxxString) -> *const u8 {
381 let vec = unsafe { &*ptr::from_ref(this).cast::<Vec<u8>>() };
382 vec.as_ptr()
383 }
384
385 #[unsafe(export_name = "cxxbridge1$cxx_string$length")]
386 unsafe extern "C" fn string_length(this: &CxxString) -> usize {
387 let vec = unsafe { &*ptr::from_ref(this).cast::<Vec<u8>>() };
388 vec.len()
389 }
390
391 #[unsafe(export_name = "cxxbridge1$cxx_string$clear")]
392 unsafe extern "C" fn string_clear(this: Pin<&mut CxxString>) {
393 let vec = unsafe { &mut *ptr::from_mut(this.get_unchecked_mut()).cast::<Vec<u8>>() };
394 vec.clear();
395 }
396
397 #[unsafe(export_name = "cxxbridge1$cxx_string$reserve_total")]
398 unsafe extern "C" fn string_reserve_total(this: Pin<&mut CxxString>, new_cap: usize) {
399 let vec = unsafe { &mut *ptr::from_mut(this.get_unchecked_mut()).cast::<Vec<u8>>() };
400 vec.reserve(new_cap.saturating_sub(vec.len()));
401 }
402
403 #[unsafe(export_name = "cxxbridge1$cxx_string$push")]
404 unsafe extern "C" fn string_push(this: Pin<&mut CxxString>, ptr: *const u8, len: usize) {
405 let vec = unsafe { &mut *ptr::from_mut(this.get_unchecked_mut()).cast::<Vec<u8>>() };
406 vec.extend_from_slice(unsafe { slice::from_raw_parts(ptr, len) });
407 }
408}