Skip to content

Preserve &[T]'s Rust representation in rust::Slice #647

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 3 commits into from
Jan 2, 2021
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
35 changes: 19 additions & 16 deletions include/cxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,11 @@ class Slice final
void swap(Slice &) noexcept;

private:
// Not necessarily ABI compatible with &[T]. Codegen will translate to
// cxx::rust_slice::RustSlice which matches this layout.
void *ptr;
std::size_t len;
friend void sliceInit(void *, const void *, std::size_t) noexcept;
friend void *slicePtr(const void *) noexcept;
friend std::size_t sliceLen(const void *) noexcept;

std::array<std::uintptr_t, 2> repr;
};

template <typename T>
Expand Down Expand Up @@ -489,37 +490,39 @@ constexpr unsafe_bitcopy_t unsafe_bitcopy{};
#ifndef CXXBRIDGE1_RUST_SLICE
#define CXXBRIDGE1_RUST_SLICE
template <typename T>
Slice<T>::Slice() noexcept
: ptr(reinterpret_cast<void *>(align_of<T>())), len(0) {}
Slice<T>::Slice() noexcept {
sliceInit(this, reinterpret_cast<void *>(align_of<T>()), 0);
}

template <typename T>
Slice<T>::Slice(T *s, std::size_t count) noexcept
: ptr(const_cast<typename std::remove_const<T>::type *>(s)), len(count) {}
Slice<T>::Slice(T *s, std::size_t count) noexcept {
sliceInit(this, const_cast<typename std::remove_const<T>::type *>(s), count);
}

template <typename T>
T *Slice<T>::data() const noexcept {
return reinterpret_cast<T *>(this->ptr);
return reinterpret_cast<T *>(slicePtr(this));
}

template <typename T>
std::size_t Slice<T>::size() const noexcept {
return this->len;
return sliceLen(this);
}

template <typename T>
std::size_t Slice<T>::length() const noexcept {
return this->len;
return this->size();
}

template <typename T>
bool Slice<T>::empty() const noexcept {
return this->len == 0;
return this->size() == 0;
}

template <typename T>
T &Slice<T>::operator[](std::size_t n) const noexcept {
assert(n < this->size());
auto pos = static_cast<char *>(this->ptr) + size_of<T>() * n;
auto pos = static_cast<char *>(slicePtr(this)) + size_of<T>() * n;
return *reinterpret_cast<T *>(pos);
}

Expand All @@ -540,7 +543,7 @@ T &Slice<T>::front() const noexcept {
template <typename T>
T &Slice<T>::back() const noexcept {
assert(!this->empty());
return (*this)[this->len - 1];
return (*this)[this->size() - 1];
}

template <typename T>
Expand Down Expand Up @@ -659,15 +662,15 @@ bool Slice<T>::iterator::operator>=(const iterator &other) const noexcept {
template <typename T>
typename Slice<T>::iterator Slice<T>::begin() const noexcept {
iterator it;
it.pos = this->ptr;
it.pos = slicePtr(this);
it.stride = size_of<T>();
return it;
}

template <typename T>
typename Slice<T>::iterator Slice<T>::end() const noexcept {
iterator it = this->begin();
it.pos = static_cast<char *>(it.pos) + it.stride * this->len;
it.pos = static_cast<char *>(it.pos) + it.stride * this->size();
return it;
}

Expand Down
16 changes: 16 additions & 0 deletions src/cxx.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ bool cxxbridge1$str$from(rust::Str *self, const char *ptr,
std::size_t len) noexcept;
const char *cxxbridge1$str$ptr(const rust::Str *self) noexcept;
std::size_t cxxbridge1$str$len(const rust::Str *self) noexcept;

// rust::Slice
void cxxbridge1$slice$new(void *self, const void *ptr,
std::size_t len) noexcept;
void *cxxbridge1$slice$ptr(const void *self) noexcept;
std::size_t cxxbridge1$slice$len(const void *self) noexcept;
} // extern "C"

namespace rust {
Expand Down Expand Up @@ -274,6 +280,16 @@ std::ostream &operator<<(std::ostream &os, const Str &s) {
return os;
}

void sliceInit(void *self, const void *ptr, std::size_t len) noexcept {
cxxbridge1$slice$new(self, ptr, len);
}

void *slicePtr(const void *self) noexcept { return cxxbridge1$slice$ptr(self); }

std::size_t sliceLen(const void *self) noexcept {
return cxxbridge1$slice$len(self);
}

// Rust specifies that usize is ABI compatible with C's uintptr_t.
// https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize
// However there is no direct Rust equivalent for size_t. C does not guarantee
Expand Down
27 changes: 14 additions & 13 deletions src/rust_slice.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
use core::mem;
use core::ptr::NonNull;
use core::ptr::{self, NonNull};
use core::slice;

// Not necessarily ABI compatible with &[T]. Codegen performs the translation.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct RustSlice {
pub(crate) ptr: NonNull<()>,
pub(crate) len: usize,
pub(crate) repr: NonNull<[()]>,
}

impl RustSlice {
pub fn from_ref<T>(s: &[T]) -> Self {
pub fn from_ref<T>(slice: &[T]) -> Self {
let ptr = ptr::slice_from_raw_parts::<()>(slice.as_ptr().cast(), slice.len());
RustSlice {
ptr: NonNull::from(s).cast::<()>(),
len: s.len(),
repr: unsafe { NonNull::new_unchecked(ptr as *mut _) },
}
}

pub fn from_mut<T>(s: &mut [T]) -> Self {
pub fn from_mut<T>(slice: &mut [T]) -> Self {
let ptr = ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().cast(), slice.len());
RustSlice {
len: s.len(),
ptr: NonNull::from(s).cast::<()>(),
repr: unsafe { NonNull::new_unchecked(ptr) },
}
}

pub unsafe fn as_slice<'a, T>(self) -> &'a [T] {
slice::from_raw_parts(self.ptr.as_ptr().cast::<T>(), self.len)
let ptr = self.repr.as_ptr();
let len = self.repr.as_ref().len();
slice::from_raw_parts(ptr.cast(), len)
}

pub unsafe fn as_mut_slice<'a, T>(self) -> &'a mut [T] {
slice::from_raw_parts_mut(self.ptr.as_ptr().cast::<T>(), self.len)
let ptr = self.repr.as_ptr();
let len = self.repr.as_ref().len();
slice::from_raw_parts_mut(ptr.cast(), len)
}
}

Expand Down
1 change: 1 addition & 0 deletions src/symbols/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod exception;
mod rust_slice;
mod rust_str;
mod rust_string;
mod rust_vec;
22 changes: 22 additions & 0 deletions src/symbols/rust_slice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::rust_slice::RustSlice;
use core::mem::MaybeUninit;
use core::ptr::{self, NonNull};

#[export_name = "cxxbridge1$slice$new"]
unsafe extern "C" fn slice_new(this: &mut MaybeUninit<RustSlice>, ptr: *const (), len: usize) {
let ptr = ptr::slice_from_raw_parts(ptr, len);
let rust_slice = RustSlice {
repr: NonNull::new_unchecked(ptr as *mut _),
};
ptr::write(this.as_mut_ptr(), rust_slice);
}

#[export_name = "cxxbridge1$slice$ptr"]
unsafe extern "C" fn slice_ptr(this: &RustSlice) -> *const () {
this.repr.as_ptr().cast()
}

#[export_name = "cxxbridge1$slice$len"]
unsafe extern "C" fn slice_len(this: &RustSlice) -> usize {
this.repr.as_ref().len()
}