diff --git a/Cargo.toml b/Cargo.toml index d7c92ffb0..9753490e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ license = "MIT/Apache2" exclude = ["neon.jpg"] [dependencies] +cslice = { version = "=0.1.0", path = "crates/cslice" } neon-sys = { version = "=0.1.8", path = "crates/neon-sys" } diff --git a/crates/cslice/src/lib.rs b/crates/cslice/src/lib.rs new file mode 100644 index 000000000..cf0892461 --- /dev/null +++ b/crates/cslice/src/lib.rs @@ -0,0 +1,200 @@ +//! A library of _C-slices_: slices with a stable ABI for interfacing with C. +//! +//! This library provides two types, `CSlice` and `CMutSlice`, for communicating +//! with C about Rust slices or foreign slice-like data structures. Both types +//! have a stable ABI consisting of exactly two pointer-sized words: +//! +//! ```c +//! struct { +//! void *base; +//! size_t len; +//! } +//! ``` +//! +//! C-slices and Rust slices are interchangeable, with conversion methods in both +//! directions. +//! +//! This makes it possible to construct slices from foreign code, as well as to +//! communicate Rust slices to foreign code conveniently. + +use std::{ptr, slice}; +use std::marker::PhantomData; +use std::ops::{Index, IndexMut}; + +/// An immutable slice, equivalent to `&'a T`. +/// +/// A `CSlice` can be constructed from a corresponding Rust slice via the `AsCSlice` trait. +/// +/// A Rust slice can be constructed from a corresponding `CSlice` via `as_ref`. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CSlice<'a, T> { + base: *const T, + len: usize, + marker: PhantomData<&'a ()> +} + +impl<'a, T> CSlice<'a, T> { + /// Create a `CSlice` from raw data. + /// + /// # Safety + /// + /// The region of memory from `base` (inclusive) to `base + len * sizeof` + /// (exclusive) must be valid for the duration of lifetime `'a`. + pub unsafe fn new(base: *const T, len: usize) -> Self { + assert!(base != ptr::null()); + CSlice { + base: base, + len: len, + marker: PhantomData + } + } + + /// Produces a raw pointer to the slice's buffer. + pub fn as_ptr(&self) -> *const T { + self.base + } + + /// Returns the number of elements in the slice. + pub fn len(&self) -> usize { + self.len + } +} + +impl<'a, T> AsRef<[T]> for CSlice<'a, T> { + fn as_ref(&self) -> &[T] { + unsafe { + slice::from_raw_parts(self.base, self.len) + } + } +} + +/// A mutable slice, equivalent to `&'a mut T`. +/// +/// A `CMutSlice` can be constructed from a corresponding Rust slice via the `AsCMutSlice` trait. +/// +/// A Rust slice can be constructed from a corresponding `CMutSlice` via `as_mut`. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CMutSlice<'a, T> { + base: *mut T, + len: usize, + marker: PhantomData<&'a ()> +} + +impl<'a, T> CMutSlice<'a, T> { + /// Create a `CSlice` from raw data. + /// + /// # Safety + /// + /// The region of memory from `base` (inclusive) to `base + len * sizeof` + /// (exclusive) must be valid for the duration of lifetime `'a`. + pub unsafe fn new(base: *mut T, len: usize) -> Self { + assert!(base != ptr::null_mut()); + CMutSlice { + base: base, + len: len, + marker: PhantomData + } + } + + /// Produces a raw pointer to the slice's buffer. + pub fn as_ptr(&self) -> *const T { + self.base + } + + /// Produces a raw pointer to the slice's buffer. + pub fn as_mut_ptr(&mut self) -> *mut T { + self.base + } + + /// Returns the number of elements in the slice. + pub fn len(&self) -> usize { + self.len + } +} + +impl<'a, T> AsRef<[T]> for CMutSlice<'a, T> { + fn as_ref(&self) -> &[T] { + unsafe { + slice::from_raw_parts(self.base, self.len) + } + } +} + +impl<'a, T> AsMut<[T]> for CMutSlice<'a, T> { + fn as_mut(&mut self) -> &mut [T] { + unsafe { + slice::from_raw_parts_mut(self.base, self.len) + } + } +} + +unsafe impl<'a, T> Sync for CSlice<'a, T> { } + +unsafe impl<'a, T> Sync for CMutSlice<'a, T> { } + + +impl<'a, T> Index for CSlice<'a, T> { + type Output = T; + + fn index(&self, i: usize) -> &T { + self.as_ref().index(i) + } +} + +impl<'a, T> Index for CMutSlice<'a, T> { + type Output = T; + + fn index(&self, i: usize) -> &T { + self.as_ref().index(i) + } +} + +impl<'a, T> IndexMut for CMutSlice<'a, T> { + fn index_mut(&mut self, i: usize) -> &mut T { + self.as_mut().index_mut(i) + } +} + +/// A cheap conversion to a `CSlice`. +pub trait AsCSlice<'a, T> { + /// Performs the conversion. + fn as_c_slice(&'a self) -> CSlice<'a, T>; +} + +/// A cheap conversion to a `CMutSlice`. +pub trait AsCMutSlice<'a, T> { + /// Performs the conversion. + fn as_c_mut_slice(&'a mut self) -> CMutSlice<'a, T>; +} + +impl<'a> AsCSlice<'a, u8> for str { + fn as_c_slice(&'a self) -> CSlice<'a, u8> { + CSlice { + base: self.as_ptr(), + len: self.len(), + marker: PhantomData + } + } +} + +impl<'a, T> AsCSlice<'a, T> for [T] { + fn as_c_slice(&'a self) -> CSlice<'a, T> { + CSlice { + base: self.as_ptr(), + len: self.len(), + marker: PhantomData + } + } +} + +impl<'a, T> AsCMutSlice<'a, T> for [T] { + fn as_c_mut_slice(&'a mut self) -> CMutSlice<'a, T> { + CMutSlice { + base: self.as_mut_ptr(), + len: self.len(), + marker: PhantomData + } + } +} diff --git a/crates/neon-sys/Cargo.toml b/crates/neon-sys/Cargo.toml index 06387c414..d56cd5772 100644 --- a/crates/neon-sys/Cargo.toml +++ b/crates/neon-sys/Cargo.toml @@ -12,5 +12,8 @@ links = "neon" # this script builds libneon.a build = "build.rs" +[dependencies] +cslice = { version = "=0.1.0", path = "../cslice" } + [build-dependencies] gcc = "0.3" diff --git a/crates/neon-sys/src/buf.rs b/crates/neon-sys/src/buf.rs deleted file mode 100644 index 9b745a9e2..000000000 --- a/crates/neon-sys/src/buf.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::{ptr, slice}; -use std::marker::PhantomData; -use std::mem; -use std::str; - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct Buf<'a> { - ptr: *mut u8, - len: usize, - marker: PhantomData<&'a ()> -} - -impl<'a> Buf<'a> { - pub fn wrap(s: &'a str) -> Buf<'a> { - Buf { - ptr: s.as_ptr() as *mut u8, - len: s.len(), - marker: PhantomData, - } - } - - pub fn as_str(self) -> Option<&'a str> { - if self.ptr == ptr::null_mut() { - return None; - } - - unsafe { - let s = slice::from_raw_parts(self.ptr as *const u8, self.len); - str::from_utf8(s).ok() - } - } - - pub fn as_slice(&self) -> Option<&'a [u8]> { - if self.ptr.is_null() { - return None; - } - - unsafe { - Some(slice::from_raw_parts(self.ptr, self.len)) - } - } - - pub fn as_mut_slice(&mut self) -> Option<&'a mut [u8]> { - if self.ptr.is_null() { - return None; - } - - unsafe { - Some(slice::from_raw_parts_mut(self.ptr, self.len)) - } - } - - pub fn as_ptr(&self) -> *const u8 { - self.ptr - } - - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self.ptr - } - - pub fn len(&self) -> usize { - self.len - } - - pub unsafe fn set_len(&mut self, len: usize) { - self.len = len; - } -} - -unsafe impl<'a> Sync for Buf<'a> { } diff --git a/crates/neon-sys/src/buffer.rs b/crates/neon-sys/src/buffer.rs index c271878ef..a6100ceed 100644 --- a/crates/neon-sys/src/buffer.rs +++ b/crates/neon-sys/src/buffer.rs @@ -1,12 +1,14 @@ use raw::Local; -use buf::Buf; +use cslice::CMutSlice; +// Suppress a spurious rustc warning about the use of CMutSlice. +#[allow(improper_ctypes)] extern "system" { #[link_name = "NeonSys_Buffer_New"] pub fn new(out: &mut Local, size: u32) -> bool; #[link_name = "NeonSys_Buffer_Data"] - pub fn data<'a, 'b>(out: &'a mut Buf<'b>, obj: Local); + pub fn data<'a, 'b>(out: &'a mut CMutSlice<'b, u8>, obj: Local); } diff --git a/crates/neon-sys/src/lib.rs b/crates/neon-sys/src/lib.rs index e92244bb6..f92e6ccc9 100644 --- a/crates/neon-sys/src/lib.rs +++ b/crates/neon-sys/src/lib.rs @@ -1,5 +1,6 @@ +extern crate cslice; + pub mod raw; -pub mod buf; pub mod call; pub mod scope; pub mod object; diff --git a/src/internal/js/binary.rs b/src/internal/js/binary.rs index cea0bf3ed..d9a375292 100644 --- a/src/internal/js/binary.rs +++ b/src/internal/js/binary.rs @@ -4,9 +4,9 @@ use internal::js::{Value, ValueInternal, Object, build}; use internal::mem::{Handle, Managed}; use internal::vm::{Lock, LockState}; use scope::Scope; +use cslice::CMutSlice; use neon_sys; use neon_sys::raw; -use neon_sys::buf::Buf; #[repr(C)] #[derive(Clone, Copy)] @@ -38,12 +38,12 @@ impl Object for JsBuffer { } // tighten the lifetime of the exposed internals not to outlive the // lock. impl<'a> Lock for Handle<'a, JsBuffer> { - type Internals = Buf<'a>; + type Internals = CMutSlice<'a, u8>; unsafe fn expose(self, state: &mut LockState) -> Self::Internals { let mut result = mem::uninitialized(); neon_sys::buffer::data(&mut result, self.to_raw()); - state.use_buffer(&result); + state.use_buffer(result); result } } diff --git a/src/internal/vm.rs b/src/internal/vm.rs index 87b7d090b..4b174a1cd 100644 --- a/src/internal/vm.rs +++ b/src/internal/vm.rs @@ -3,9 +3,9 @@ use std::any::TypeId; use std::marker::PhantomData; use std::collections::{HashSet, HashMap}; use std::os::raw::c_void; +use cslice::CMutSlice; use neon_sys; use neon_sys::raw; -use neon_sys::buf::Buf; use internal::scope::{Scope, RootScope, RootScopeInternal}; use internal::js::{JsValue, Value, Object, JsObject, JsFunction}; use internal::js::class::ClassMetadata; @@ -286,7 +286,7 @@ pub struct LockState { } impl LockState { - pub fn use_buffer(&mut self, buf: &Buf) { + pub fn use_buffer(&mut self, buf: CMutSlice) { let p = buf.as_ptr() as usize; if !self.buffers.insert(p) { panic!("attempt to lock heap with duplicate buffers (0x{:x})", p); diff --git a/src/lib.rs b/src/lib.rs index 6eaad0293..9b4a97174 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ //! The `neon` crate provides the entire [Neon](http://neon.rustbridge.io) API. extern crate neon_sys; +extern crate cslice; mod internal; pub mod mem;