Skip to content

Support &mut [u8] #502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ returns of functions.
<tr><td>String</td><td>rust::String</td><td></td></tr>
<tr><td>&amp;str</td><td>rust::Str</td><td></td></tr>
<tr><td>&amp;[u8]</td><td>rust::Slice&lt;const uint8_t&gt;</td><td><sup><i>arbitrary &amp;[T] not implemented yet</i></sup></td></tr>
<tr><td>&amp;mut [u8]</td><td>rust::Slice&lt;uint8_t&gt;</td><td><sup><i>arbitrary &amp;mut [T] not implemented yet</i></sup></td></tr>
<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
<tr><td>Box&lt;T&gt;</td><td>rust::Box&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
Expand Down
2 changes: 1 addition & 1 deletion book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
- [Reference: built-in bindings](bindings.md)
- [String &mdash; rust::String](binding/string.md)
- [&str &mdash; rust::Str](binding/str.md)
- [&&#91;T&#93; &mdash; rust::Slice\<const T\>](binding/slice.md)
- [&&#91;T&#93;, &mut &#91;T&#93; &mdash; rust::Slice\<T\>](binding/slice.md)
- [CxxString &mdash; std::string](binding/cxxstring.md)
- [Box\<T\> &mdash; rust::Box\<T\>](binding/box.md)
- [UniquePtr\<T\> &mdash; std::unique\_ptr\<T\>](binding/uniqueptr.md)
Expand Down
17 changes: 13 additions & 4 deletions book/src/binding/slice.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{{#title rust::Slice<T> — Rust ♡ C++}}
# rust::Slice\<T\>
# rust::Slice\<const T\>,&ensp;rust::Slice\<T\>

- Rust `&[T]` is written `rust::Slice<const T>` in C++
- Rust `&mut [T]` is written `rust::Slice<T>` in C++

### Public API:

Expand All @@ -15,7 +18,10 @@ public:
Slice(const Slice<T> &) noexcept;
Slice(T *, size_t count) noexcept;

// if std::is_const<T> {
Slice &operator=(const Slice<T> &) noexcept;
// }
Slice &operator=(Slice<T> &&) noexcept;

T *data() const noexcept;
size_t size() const noexcept;
Expand All @@ -27,11 +33,14 @@ public:

### Restrictions:

Only &amp;\[u8\] i.e. rust::Slice\<const uint8\_t\> is currently implemented.
Support for arbitrary &amp;\[T\] is coming.
Only T=u8 i.e. rust::Slice\<const uint8\_t\> and rust::Slice\<uint8\_t\> are
currently implemented. Support for arbitrary T is coming.

Allowed as function argument or return value. Not supported in shared structs.
&amp;mut \[T\] is not supported yet.

Only rust::Slice\<const T\> is copy-assignable, not rust::Slice\<T\>. (Both are
move-assignable.) You'll need to write std::move occasionally as a reminder that
accidentally exposing overlapping &amp;mut \[T\] to Rust is UB.

## Example

Expand Down
2 changes: 1 addition & 1 deletion book/src/bindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ returns of extern functions.
<tr><td style="padding:3px 6px">String</td><td style="padding:3px 6px"><b><a href="binding/string.md">rust::String</a></b></td><td style="padding:3px 6px"></td></tr>
<tr><td style="padding:3px 6px">&amp;str</td><td style="padding:3px 6px"><b><a href="binding/str.md">rust::Str</a></b></td><td style="padding:3px 6px"></td></tr>
<tr><td style="padding:3px 6px">&amp;[u8]</td><td style="padding:3px 6px"><b><a href="binding/slice.md">rust::Slice&lt;const&nbsp;uint8_t&gt;</a></b></td><td style="padding:3px 6px"><sup><i>arbitrary &amp;[T] not implemented yet</i></sup></td></tr>
<tr><td style="padding:3px 6px">&amp;mut [u8]</td><td style="padding:3px 6px"><b><a href="binding/slice.md">rust::Slice&lt;uint8_t&gt;</a></b></td><td style="padding:3px 6px"><sup><i>arbitrary &amp;mut [T] not implemented yet</i></sup></td></tr>
<tr><td style="padding:3px 6px"><b><a href="binding/cxxstring.md">CxxString</a></b></td><td style="padding:3px 6px">std::string</td><td style="padding:3px 6px"><sup><i>cannot be passed by value</i></sup></td></tr>
<tr><td style="padding:3px 6px">Box&lt;T&gt;</td><td style="padding:3px 6px"><b><a href="binding/box.md">rust::Box&lt;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
<tr><td style="padding:3px 6px"><b><a href="binding/uniqueptr.md">UniquePtr&lt;T&gt;</a></b></td><td style="padding:3px 6px">std::unique_ptr&lt;T&gt;</td><td style="padding:3px 6px"><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
Expand Down Expand Up @@ -43,7 +44,6 @@ matter of designing a nice API for each in its non-native language.

<table>
<tr><th>name in Rust</th><th>name in C++</th></tr>
<tr><td>&amp;mut [T]</td><td>rust::Slice&lt;T&gt;</td></tr>
<tr><td>BTreeMap&lt;K, V&gt;</td><td><sup><i>tbd</i></sup></td></tr>
<tr><td>HashMap&lt;K, V&gt;</td><td><sup><i>tbd</i></sup></td></tr>
<tr><td>Arc&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
Expand Down
20 changes: 13 additions & 7 deletions gen/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ pub(super) fn write(out: &mut OutFile) {
if builtin.ptr_len {
out.begin_block(Block::Namespace("repr"));
writeln!(out, "struct PtrLen final {{");
writeln!(out, " const void *ptr;");
writeln!(out, " void *ptr;");
writeln!(out, " size_t len;");
writeln!(out, "}};");
out.end_block(Block::Namespace("repr"));
Expand All @@ -173,7 +173,10 @@ pub(super) fn write(out: &mut OutFile) {
}
if builtin.rust_str_repr {
writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{");
writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};");
writeln!(
out,
" return repr::PtrLen{{const_cast<char *>(str.ptr), str.len}};",
);
writeln!(out, " }}");
}
writeln!(out, "}};");
Expand All @@ -189,18 +192,21 @@ pub(super) fn write(out: &mut OutFile) {
out,
" static Slice<T> slice(repr::PtrLen repr) noexcept {{",
);
writeln!(
out,
" return {{static_cast<const T *>(repr.ptr), repr.len}};",
);
writeln!(out, " return {{static_cast<T *>(repr.ptr), repr.len}};");
writeln!(out, " }}");
}
if builtin.rust_slice_repr {
include.type_traits = true;
writeln!(
out,
" static repr::PtrLen repr(Slice<T> slice) noexcept {{",
);
writeln!(out, " return repr::PtrLen{{slice.ptr, slice.len}};");
writeln!(out, " return repr::PtrLen{{");
writeln!(
out,
" const_cast<typename ::std::remove_const<T>::type *>(slice.ptr),",
);
writeln!(out, " slice.len}};");
writeln!(out, " }}");
}
writeln!(out, "}};");
Expand Down
37 changes: 24 additions & 13 deletions gen/src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,9 +436,11 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
out.builtin.rust_str_repr = true;
write!(out, "::rust::impl<::rust::Str>::repr(");
}
Some(Type::SliceRefU8(_)) if !indirect_return => {
Some(ty @ Type::SliceRefU8(_)) if !indirect_return => {
out.builtin.rust_slice_repr = true;
write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::repr(")
write!(out, "::rust::impl<");
write_type(out, ty);
write!(out, ">::repr(");
}
_ => {}
}
Expand Down Expand Up @@ -474,12 +476,13 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
out.builtin.unsafe_bitcopy = true;
write_type(out, &arg.ty);
write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident);
} else if let Type::SliceRefU8(_) = arg.ty {
write!(
out,
"::rust::Slice<const uint8_t>(static_cast<const uint8_t *>({0}.ptr), {0}.len)",
arg.ident,
);
} else if let Type::SliceRefU8(slice) = &arg.ty {
write_type(out, &arg.ty);
write!(out, "(static_cast<");
if slice.mutability.is_none() {
write!(out, "const ");
}
write!(out, "uint8_t *>({0}.ptr), {0}.len)", arg.ident);
} else if out.types.needs_indirect_abi(&arg.ty) {
out.include.utility = true;
write!(out, "::std::move(*{})", arg.ident);
Expand Down Expand Up @@ -507,7 +510,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
writeln!(out, " throw$.len = ::std::strlen(catch$);");
writeln!(
out,
" throw$.ptr = ::cxxbridge1$exception(catch$, throw$.len);",
" throw$.ptr = const_cast<char *>(::cxxbridge1$exception(catch$, throw$.len));",
);
writeln!(out, " }});");
writeln!(out, " return throw$;");
Expand Down Expand Up @@ -694,7 +697,9 @@ fn write_rust_function_shim_impl(
}
Type::SliceRefU8(_) => {
out.builtin.rust_slice_new = true;
write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::slice(");
write!(out, "::rust::impl<");
write_type(out, ret);
write!(out, ">::slice(");
}
_ => {}
}
Expand All @@ -720,7 +725,9 @@ fn write_rust_function_shim_impl(
}
Type::SliceRefU8(_) => {
out.builtin.rust_slice_repr = true;
write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::repr(");
write!(out, "::rust::impl<");
write_type(out, &arg.ty);
write!(out, ">::repr(");
}
ty if out.types.needs_indirect_abi(ty) => write!(out, "&"),
_ => {}
Expand Down Expand Up @@ -890,8 +897,12 @@ fn write_type(out: &mut OutFile, ty: &Type) {
Type::Str(_) => {
write!(out, "::rust::Str");
}
Type::SliceRefU8(_) => {
write!(out, "::rust::Slice<const uint8_t>");
Type::SliceRefU8(ty) => {
write!(out, "::rust::Slice<");
if ty.mutability.is_none() {
write!(out, "const ");
}
write!(out, "uint8_t>");
}
Type::Fn(f) => {
write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" });
Expand Down
20 changes: 16 additions & 4 deletions include/cxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,28 @@ class Str final {
#endif // CXXBRIDGE1_RUST_STR

#ifndef CXXBRIDGE1_RUST_SLICE
template <typename T>
class Slice final {
static_assert(std::is_const<T>::value,
"&[T] needs to be written as rust::Slice<const T> in C++");
namespace detail {
template <bool cond>
struct copy_assignable_if {};

template <>
struct copy_assignable_if<false> {
copy_assignable_if() noexcept = default;
copy_assignable_if(const copy_assignable_if &) noexcept = default;
copy_assignable_if &operator=(const copy_assignable_if &) noexcept = delete;
copy_assignable_if &operator=(copy_assignable_if &&) noexcept = default;
};
} // namespace detail

template <typename T>
class Slice final
: private detail::copy_assignable_if<std::is_const<T>::value> {
public:
Slice() noexcept;
Slice(T *, size_t count) noexcept;

Slice &operator=(const Slice<T> &) noexcept = default;
Slice &operator=(Slice<T> &&) noexcept = default;

T *data() const noexcept;
size_t size() const noexcept;
Expand Down
20 changes: 16 additions & 4 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,10 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
_ => quote!(#var),
},
Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)),
Type::SliceRefU8(ty) => match ty.mutable {
false => quote!(::cxx::private::RustSliceU8::from_ref(#var)),
true => quote!(::cxx::private::RustSliceU8::from_mut(#var)),
},
ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
_ => quote!(#var),
}
Expand Down Expand Up @@ -423,7 +426,10 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
_ => call,
},
Type::Str(_) => quote!(#call.as_str()),
Type::SliceRefU8(_) => quote!(#call.as_slice()),
Type::SliceRefU8(ty) => match ty.mutable {
false => quote!(#call.as_slice()),
true => quote!(#call.as_mut_slice()),
},
_ => call,
},
};
Expand Down Expand Up @@ -610,7 +616,10 @@ fn expand_rust_function_shim_impl(
_ => quote!(#ident),
},
Type::Str(_) => quote!(#ident.as_str()),
Type::SliceRefU8(_) => quote!(#ident.as_slice()),
Type::SliceRefU8(ty) => match ty.mutable {
false => quote!(#ident.as_slice()),
true => quote!(#ident.as_mut_slice()),
},
ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
_ => quote!(#ident),
}
Expand Down Expand Up @@ -654,7 +663,10 @@ fn expand_rust_function_shim_impl(
_ => None,
},
Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)),
Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from)),
Type::SliceRefU8(ty) => match ty.mutable {
false => Some(quote!(::cxx::private::RustSliceU8::from_ref)),
true => Some(quote!(::cxx::private::RustSliceU8::from_mut)),
},
_ => None,
});

Expand Down
11 changes: 11 additions & 0 deletions src/cxx.cc
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,17 @@ static_assert(std::is_trivially_move_assignable<Slice<const uint8_t>>::value,
static_assert(std::is_trivially_destructible<Slice<const uint8_t>>::value,
"trivial ~Slice()");

static_assert(std::is_trivially_copy_constructible<Slice<uint8_t>>::value,
"trivial Slice(const Slice &)");
static_assert(std::is_trivially_move_constructible<Slice<uint8_t>>::value,
"trivial Slice(Slice &&)");
static_assert(!std::is_copy_assignable<Slice<uint8_t>>::value,
"delete Slice::operator=(const Slice &) for mut slices");
static_assert(std::is_trivially_move_assignable<Slice<uint8_t>>::value,
"trivial Slice::operator=(Slice &&)");
static_assert(std::is_trivially_destructible<Slice<uint8_t>>::value,
"trivial ~Slice()");

extern "C" {
const char *cxxbridge1$error(const char *ptr, size_t len) {
char *copy = new char[len];
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@
//! <tr><td>String</td><td>rust::String</td><td></td></tr>
//! <tr><td>&amp;str</td><td>rust::Str</td><td></td></tr>
//! <tr><td>&amp;[u8]</td><td>rust::Slice&lt;const uint8_t&gt;</td><td><sup><i>arbitrary &amp;[T] not implemented yet</i></sup></td></tr>
//! <tr><td>&amp;mut [u8]</td><td>rust::Slice&lt;uint8_t&gt;</td><td><sup><i>arbitrary &amp;mut [T] not implemented yet</i></sup></td></tr>
//! <tr><td><a href="struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
//! <tr><td>Box&lt;T&gt;</td><td>rust::Box&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
//! <tr><td><a href="struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
Expand Down
13 changes: 12 additions & 1 deletion src/rust_sliceu8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,27 @@ pub struct RustSliceU8 {
}

impl RustSliceU8 {
pub fn from(s: &[u8]) -> Self {
pub fn from_ref(s: &[u8]) -> Self {
RustSliceU8 {
ptr: NonNull::from(s).cast::<u8>(),
len: s.len(),
}
}

pub fn from_mut(s: &mut [u8]) -> Self {
RustSliceU8 {
len: s.len(),
ptr: NonNull::from(s).cast::<u8>(),
}
}

pub unsafe fn as_slice<'a>(self) -> &'a [u8] {
slice::from_raw_parts(self.ptr.as_ptr(), self.len)
}

pub unsafe fn as_mut_slice<'a>(self) -> &'a mut [u8] {
slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len)
}
}

const_assert_eq!(
Expand Down
10 changes: 8 additions & 2 deletions syntax/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ fn check_type_ref(cx: &mut Check, ty: &Ref) {
}

fn check_type_slice(cx: &mut Check, ty: &Slice) {
cx.error(ty, "only &[u8] is supported so far, not other slice types");
cx.error(
ty,
"only &[u8] and &mut [u8] are supported so far, not other slice types",
);
}

fn check_type_fn(cx: &mut Check, ty: &Signature) {
Expand Down Expand Up @@ -489,7 +492,10 @@ fn describe(cx: &mut Check, ty: &Type) -> String {
Type::Str(_) => "&str".to_owned(),
Type::CxxVector(_) => "C++ vector".to_owned(),
Type::Slice(_) => "slice".to_owned(),
Type::SliceRefU8(_) => "&[u8]".to_owned(),
Type::SliceRefU8(ty) => match ty.mutable {
false => "&[u8]".to_owned(),
true => "&mut [u8]".to_owned(),
},
Type::Fn(_) => "function pointer".to_owned(),
Type::Void(_) => "()".to_owned(),
}
Expand Down
2 changes: 1 addition & 1 deletion syntax/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ fn parse_type_reference(ty: &TypeReference, namespace: &Namespace) -> Result<Typ
}
}
Type::Slice(slice) => match &slice.inner {
Type::Ident(ident) if ident.rust == U8 && ty.mutability.is_none() => Type::SliceRefU8,
Type::Ident(ident) if ident.rust == U8 => Type::SliceRefU8,
_ => Type::Ref,
},
_ => Type::Ref,
Expand Down
Loading