Skip to content

Commit d1812ba

Browse files
authored
Merge pull request #645 from dtolnay/str
Preserve &str's original Rust representation in C++
2 parents aec3f24 + 1202de5 commit d1812ba

File tree

4 files changed

+67
-45
lines changed

4 files changed

+67
-45
lines changed

include/cxx.h

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class String final {
8888
#endif // CXXBRIDGE1_RUST_STRING
8989

9090
#ifndef CXXBRIDGE1_RUST_STR
91+
#define CXXBRIDGE1_RUST_STR
9192
// https://cxx.rs/binding/str.html
9293
class Str final {
9394
public:
@@ -125,10 +126,7 @@ class Str final {
125126
bool operator>=(const Str &) const noexcept;
126127

127128
private:
128-
// Not necessarily ABI compatible with &str. Codegen will translate to
129-
// cxx::rust_str::RustStr which matches this layout.
130-
const char *ptr;
131-
std::size_t len;
129+
std::array<std::uintptr_t, 2> repr;
132130
};
133131
#endif // CXXBRIDGE1_RUST_STR
134132

@@ -484,15 +482,6 @@ struct unsafe_bitcopy_t final {
484482
constexpr unsafe_bitcopy_t unsafe_bitcopy{};
485483
#endif // CXXBRIDGE1_RUST_BITCOPY
486484

487-
#ifndef CXXBRIDGE1_RUST_STR
488-
#define CXXBRIDGE1_RUST_STR
489-
inline const char *Str::data() const noexcept { return this->ptr; }
490-
491-
inline std::size_t Str::size() const noexcept { return this->len; }
492-
493-
inline std::size_t Str::length() const noexcept { return this->len; }
494-
#endif // CXXBRIDGE1_RUST_STR
495-
496485
#ifndef CXXBRIDGE1_RUST_SLICE
497486
#define CXXBRIDGE1_RUST_SLICE
498487
template <typename T>

src/cxx.cc

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ std::size_t cxxbridge1$string$len(const rust::String *self) noexcept;
3939
void cxxbridge1$string$reserve_total(rust::String *self, size_t cap) noexcept;
4040

4141
// rust::Str
42-
bool cxxbridge1$str$valid(const char *ptr, std::size_t len) noexcept;
42+
void cxxbridge1$str$new(rust::Str *self) noexcept;
43+
void cxxbridge1$str$ref(rust::Str *self, const rust::String *string) noexcept;
44+
bool cxxbridge1$str$from(rust::Str *self, const char *ptr,
45+
std::size_t len) noexcept;
46+
const char *cxxbridge1$str$ptr(const rust::Str *self) noexcept;
47+
std::size_t cxxbridge1$str$len(const rust::Str *self) noexcept;
4348
} // extern "C"
4449

4550
namespace rust {
@@ -185,46 +190,52 @@ std::ostream &operator<<(std::ostream &os, const String &s) {
185190
return os;
186191
}
187192

188-
Str::Str() noexcept : ptr(reinterpret_cast<const char *>(1)), len(0) {}
193+
Str::Str() noexcept { cxxbridge1$str$new(this); }
189194

190-
Str::Str(const String &s) noexcept : ptr(s.data()), len(s.length()) {}
195+
Str::Str(const String &s) noexcept { cxxbridge1$str$ref(this, &s); }
191196

192-
static void initStr(const char *ptr, std::size_t len) {
193-
if (!cxxbridge1$str$valid(ptr, len)) {
197+
static void initStr(Str *self, const char *ptr, std::size_t len) {
198+
if (!cxxbridge1$str$from(self, ptr, len)) {
194199
panic<std::invalid_argument>("data for rust::Str is not utf-8");
195200
}
196201
}
197202

198-
Str::Str(const std::string &s) : ptr(s.data()), len(s.length()) {
199-
initStr(this->ptr, this->len);
200-
}
203+
Str::Str(const std::string &s) { initStr(this, s.data(), s.length()); }
201204

202-
Str::Str(const char *s) : ptr(s), len(std::strlen(s)) {
205+
Str::Str(const char *s) {
203206
assert(s != nullptr);
204-
initStr(this->ptr, this->len);
207+
initStr(this, s, std::strlen(s));
205208
}
206209

207-
Str::Str(const char *s, std::size_t len)
208-
: ptr(s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s),
209-
len(len) {
210+
Str::Str(const char *s, std::size_t len) {
210211
assert(s != nullptr || len == 0);
211-
initStr(this->ptr, this->len);
212+
initStr(this,
213+
s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s,
214+
len);
212215
}
213216

214217
Str::operator std::string() const {
215218
return std::string(this->data(), this->size());
216219
}
217220

221+
const char *Str::data() const noexcept { return cxxbridge1$str$ptr(this); }
222+
223+
std::size_t Str::size() const noexcept { return cxxbridge1$str$len(this); }
224+
225+
std::size_t Str::length() const noexcept { return this->size(); }
226+
218227
Str::const_iterator Str::begin() const noexcept { return this->cbegin(); }
219228

220229
Str::const_iterator Str::end() const noexcept { return this->cend(); }
221230

222-
Str::const_iterator Str::cbegin() const noexcept { return this->ptr; }
231+
Str::const_iterator Str::cbegin() const noexcept { return this->data(); }
223232

224-
Str::const_iterator Str::cend() const noexcept { return this->ptr + this->len; }
233+
Str::const_iterator Str::cend() const noexcept {
234+
return this->data() + this->size();
235+
}
225236

226237
bool Str::operator==(const Str &rhs) const noexcept {
227-
return this->len == rhs.len &&
238+
return this->size() == rhs.size() &&
228239
std::equal(this->begin(), this->end(), rhs.begin());
229240
}
230241

src/rust_str.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,20 @@
11
use core::mem;
22
use core::ptr::NonNull;
3-
use core::slice;
43
use core::str;
54

6-
// Not necessarily ABI compatible with &str. Codegen performs the translation.
75
#[repr(C)]
8-
#[derive(Copy, Clone)]
96
pub struct RustStr {
10-
pub(crate) ptr: NonNull<u8>,
11-
pub(crate) len: usize,
7+
repr: NonNull<str>,
128
}
139

1410
impl RustStr {
15-
pub fn from(s: &str) -> Self {
16-
RustStr {
17-
ptr: NonNull::from(s).cast::<u8>(),
18-
len: s.len(),
19-
}
11+
pub fn from(repr: &str) -> Self {
12+
let repr = NonNull::from(repr);
13+
RustStr { repr }
2014
}
2115

2216
pub unsafe fn as_str<'a>(self) -> &'a str {
23-
let slice = slice::from_raw_parts(self.ptr.as_ptr(), self.len);
24-
str::from_utf8_unchecked(slice)
17+
&*self.repr.as_ptr()
2518
}
2619
}
2720

src/symbols/rust_str.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,37 @@
1+
use alloc::string::String;
2+
use core::mem::MaybeUninit;
3+
use core::ptr;
14
use core::slice;
25
use core::str;
36

4-
#[export_name = "cxxbridge1$str$valid"]
5-
unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool {
7+
#[export_name = "cxxbridge1$str$new"]
8+
unsafe extern "C" fn str_new(this: &mut MaybeUninit<&str>) {
9+
ptr::write(this.as_mut_ptr(), "");
10+
}
11+
12+
#[export_name = "cxxbridge1$str$ref"]
13+
unsafe extern "C" fn str_ref<'a>(this: &mut MaybeUninit<&'a str>, string: &'a String) {
14+
ptr::write(this.as_mut_ptr(), string.as_str());
15+
}
16+
17+
#[export_name = "cxxbridge1$str$from"]
18+
unsafe extern "C" fn str_from(this: &mut MaybeUninit<&str>, ptr: *const u8, len: usize) -> bool {
619
let slice = slice::from_raw_parts(ptr, len);
7-
str::from_utf8(slice).is_ok()
20+
match str::from_utf8(slice) {
21+
Ok(s) => {
22+
ptr::write(this.as_mut_ptr(), s);
23+
true
24+
}
25+
Err(_) => false,
26+
}
27+
}
28+
29+
#[export_name = "cxxbridge1$str$ptr"]
30+
unsafe extern "C" fn str_ptr(this: &&str) -> *const u8 {
31+
this.as_ptr()
32+
}
33+
34+
#[export_name = "cxxbridge1$str$len"]
35+
unsafe extern "C" fn str_len(this: &&str) -> usize {
36+
this.len()
837
}

0 commit comments

Comments
 (0)