diff --git a/Cargo.toml b/Cargo.toml index 0063dbd1..4065c53b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,9 +5,9 @@ version = "0.0.1" description = "Bindings to Python 2.7" authors = ["Daniel Grunwald "] -#[dependencies] -#python27-sys="0.0.2" +[dependencies] +python27-sys="0.0.3" -[dependencies.python27-sys] -path = "../python27-sys" +#[dependencies.python27-sys] +#path = "../python27-sys" diff --git a/src/cstr.rs b/src/cstr.rs deleted file mode 100644 index de7b96fa..00000000 --- a/src/cstr.rs +++ /dev/null @@ -1,149 +0,0 @@ -use libc::c_char; -use std::ffi::CString; -use std::borrow::{BorrowFrom, ToOwned}; -use std::{fmt, mem, ops, str}; - -// Idea: -// CString = Vec + 0-invariant (0-terminated, but no interior 0) -// this makes it similar to -// String = Vec + UTF-8-invariant - -// Dereferencing String results in -// str = [u8] + UTF-8 invariant - -// So why does dereferencing CString result in [u8], dropping the 0-invariant? -// This module implements a type CStr = [u8] + 0-invariant. - -// This allows writing safe FFI bindings that accept &CStr and call a C function: -// fn f(s: &CStr) { ffi::f(s.as_ptr()) } - -// Without CStr, f() would have to take &CString (forcing the string to be heap-allocated), -// or f() could take &[u8] and verify the 0-invariant itself -- but this verification is redundant -// when the &[u8] was borrowed from a CString. -// (or otherwise known to be valid, e.g. the output of a cstr!("string literal") macro) - - -// CString changes if this type is adopted in std::ffi: -// * ::Target should change from &[c_char] to &CStr -// * CString::{as_slice_with_nul, as_bytes, as_bytes_with_nul} can be deleted, as they already available through Deref -// * The free functions c_str_to_bytes and c_str_to_bytes_with_nul can be removed, as the same functionality is -// available through CStr::from_ptr( ).as_bytes() / CStr::from_ptr( ).as_bytes_with_nul() - -// Independently from CStr: -// * CString::from_slice(s.as_bytes()) looks weird, since both 'as_slice' and 'as_bytes' -// exist on CString with different return types. -// CString::from_slice should be renamed to from_bytes - -// #[derive(PartialEq, PartialOrd, Eq, Ord, Hash) -- ICE #18805 -// #[repr(newtype)] or something, for the transmute in from_slice_with_nul_unchecked -pub struct CStr { - // invariants: - // - data.len() >= 1 - // - data[0..data.len()-1] does not contain '\0' - // - data[data.len()-1] == '\0' - inner: [c_char] -} - - -impl CStr { - /// Convert a C string pointer into a &CStr reference. - /// - /// Unsafe because: - /// * The pointer is assumed to point to a valid C string. - /// * The lifetime provided may not be a suitable lifetime for the returned &CStr. - pub unsafe fn from_ptr<'a>(raw: &'a *const c_char) -> &'a CStr { - CStr::from_bytes_with_nul_unchecked(::std::ffi::c_str_to_bytes_with_nul(raw)) - } - - /// Create a C-compatible string slice from a byte slice. - /// - /// # Panics - /// - /// This function will panic if the last byte in the slice is not 0, - /// or if any other bytes are 0. - pub fn from_bytes_with_nul(v: &[u8]) -> &CStr { - assert!(v[v.len() - 1] == 0 && !v[..v.len() - 1].iter().any(|&x| x == 0)); - unsafe { CStr::from_bytes_with_nul_unchecked(v) } - } - - /// Create a C-compatible string slice from a byte slice. - /// The slice must have a length >= 1, the last byte must be 0, - /// and no other bytes may be 0. - /// - /// Violating these constraints causes undefined behavior. - pub unsafe fn from_bytes_with_nul_unchecked(v: &[u8]) -> &CStr { - // TODO: does this transmute have defined behavior? - // we're relying on repr([u8]) = repr(CStr) here, newtypes would surely be helpful - mem::transmute::<&[u8], &CStr>(v) - } - - // as_ptr(), as_slice(): should be coming from Deref, but - // we need to re-implement them to avoid an ICE (#16812 ?) - pub fn as_ptr(&self) -> *const c_char { - self.inner.as_ptr() - } - - pub fn as_slice(&self) -> &[c_char] { - &self.inner[..self.inner.len() - 1] - } - - /// Create a view into this C string which includes the trailing nul - /// terminator at the end of the string. - pub fn as_slice_with_nul(&self) -> &[c_char] { - &self.inner - } - - pub fn as_bytes(&self) -> &[u8] { - unsafe { mem::transmute(self.as_slice()) } - } - - pub fn as_bytes_with_nul(&self) -> &[u8] { - unsafe { mem::transmute(self.as_slice_with_nul()) } - } - - pub fn as_utf8(&self) -> Result<&str, str::Utf8Error> { - str::from_utf8(self.as_bytes()) - } -} - -impl ops::Deref for CStr { - type Target = [c_char]; - - fn deref(&self) -> &[c_char] { - // Does not underflow thanks to our invariant. - // But rustc doesn't know that, so it may need some help to generate efficient code. - &self.inner[..self.inner.len() - 1] - } -} - -impl BorrowFrom for CStr { - fn borrow_from(owned: &CString) -> &CStr { - // This is safe because CStr and CString have the same invariant. - unsafe { CStr::from_bytes_with_nul_unchecked(owned.as_bytes_with_nul()) } - } -} - -impl ToOwned for CStr { - fn to_owned(&self) -> CString { - // This is safe because CStr and CString have the same invariant. - unsafe { - CString::from_vec_unchecked(self.as_bytes_with_nul().to_owned()) - } - } -} - -impl fmt::Debug for CStr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - String::from_utf8_lossy(self.as_bytes()).fmt(f) - } -} - -#[macro_export] -macro_rules! cstr( - ($s: tt) => ( - // TODO: verify that $s is a string literal without nuls, - // and remove the runtime check by using from_bytes_with_nul_unchecked. - $crate::CStr::from_bytes_with_nul(concat!($s, "\0").as_bytes()) - ); -); - diff --git a/src/err.rs b/src/err.rs index 20f70c53..46e16b4b 100644 --- a/src/err.rs +++ b/src/err.rs @@ -5,7 +5,7 @@ use objects::oldstyle::PyClass; use ffi; use libc; use conversion::ToPyObject; -use cstr::CStr; +use std::ffi::CStr; /// Represents a python exception that was raised. #[derive(Clone, Debug)] @@ -248,7 +248,7 @@ mod tests { let py = gil.python(); PyErr::new_lazy_init(py.get_type::(), None).restore(); assert!(PyErr::occurred(py)); - drop(PyErr::fetch(py)) + drop(PyErr::fetch(py)); } } diff --git a/src/lib.rs b/src/lib.rs index e8c20099..1b375adf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ #![feature(core)] #![feature(libc)] -#![feature(std_misc)] #![feature(unsafe_destructor)] +#![feature(unsafe_no_drop_flag)] #![feature(optin_builtin_traits)] #![allow(unused_imports, dead_code, unused_variables)] @@ -14,10 +14,18 @@ pub use objects::*; pub use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject}; pub use conversion::{FromPyObject, ToPyObject}; pub use objectprotocol::{ObjectProtocol}; -pub use cstr::CStr; +pub use std::ffi::CStr; + +#[macro_export] +macro_rules! cstr( + ($s: tt) => ( + // TODO: verify that $s is a string literal without nuls + unsafe { + ::std::ffi::CStr::from_ptr(concat!($s, "\0").as_ptr() as *const _) + } + ); +); -#[macro_use] -mod cstr; mod python; mod err; mod conversion; diff --git a/src/objectprotocol.rs b/src/objectprotocol.rs index 73e3bc2a..92fdcd2f 100644 --- a/src/objectprotocol.rs +++ b/src/objectprotocol.rs @@ -1,5 +1,6 @@ use std; use std::{fmt, string}; +use std::borrow::Cow; use std::cmp::Ordering; use ffi; use libc; @@ -234,7 +235,7 @@ impl <'p> fmt::Debug for PyObject<'p> { fn fmt(&self, f : &mut fmt::Formatter) -> Result<(), fmt::Error> { use objectprotocol::ObjectProtocol; let repr_obj = try!(self.str().map_err(|_| fmt::Error)); - let repr = try!(repr_obj.extract::().map_err(|_| fmt::Error)); + let repr = try!(repr_obj.extract::>().map_err(|_| fmt::Error)); f.write_str(&*repr) } } @@ -243,7 +244,7 @@ impl <'p> fmt::Display for PyObject<'p> { fn fmt(&self, f : &mut fmt::Formatter) -> Result<(), fmt::Error> { use objectprotocol::ObjectProtocol; let repr_obj = try!(self.repr().map_err(|_| fmt::Error)); - let repr = try!(repr_obj.extract::().map_err(|_| fmt::Error)); + let repr = try!(repr_obj.extract::>().map_err(|_| fmt::Error)); f.write_str(&*repr) } } diff --git a/src/objects/exc.rs b/src/objects/exc.rs index eab35ecc..0f8f8a30 100644 --- a/src/objects/exc.rs +++ b/src/objects/exc.rs @@ -2,7 +2,7 @@ use libc::c_char; use std::ops::Range; use std::str::Utf8Error; use std::mem; -use cstr::CStr; +use std::ffi::CStr; use ffi; use python::{Python, ToPythonPointer, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectDowncastError, PythonObjectWithTypeObject}; use err::{self, PyResult}; diff --git a/src/objects/module.rs b/src/objects/module.rs index bc15e7d2..d11e0d1c 100644 --- a/src/objects/module.rs +++ b/src/objects/module.rs @@ -5,7 +5,7 @@ use python::{Python, PythonObject, ToPythonPointer}; use conversion::ToPyObject; use objects::{PyObject, PyType, PyDict, exc}; use err::{self, PyResult, PyErr}; -use cstr::CStr; +use std::ffi::CStr; pyobject_newtype!(PyModule, PyModule_Check, PyModule_Type); @@ -34,7 +34,7 @@ impl <'p> PyModule<'p> { Err(PyErr::fetch(py)) } else { let ptr = ptr as *const c_char; - let slice = std::ffi::c_str_to_bytes(&ptr); + let slice = CStr::from_ptr(ptr).to_bytes(); match std::str::from_utf8(slice) { Ok(s) => Ok(std::mem::copy_lifetime(self, s)), Err(e) => Err(PyErr::new(try!(exc::UnicodeDecodeError::new_utf8(py, slice, e)))) diff --git a/src/objects/string.rs b/src/objects/string.rs index 0b07b458..8db0c21d 100644 --- a/src/objects/string.rs +++ b/src/objects/string.rs @@ -2,7 +2,6 @@ use std; use std::{char, str}; use std::ascii::AsciiExt; use std::borrow::Cow; -use std::string::CowString; use libc::c_char; use ffi; use python::{Python, PythonObject, ToPythonPointer}; @@ -21,7 +20,7 @@ impl <'p> PyString<'p> { unsafe { let buffer = ffi::PyString_AS_STRING(self.as_ptr()) as *const u8; let length = ffi::PyString_GET_SIZE(self.as_ptr()) as usize; - std::slice::from_raw_buf(std::mem::copy_lifetime(self, &buffer), length) + std::slice::from_raw_parts(buffer, length) } } @@ -35,7 +34,7 @@ impl <'p> PyUnicode<'p> { unsafe { let buffer = ffi::PyUnicode_AS_UNICODE(self.as_ptr()) as *const _; let length = ffi::PyUnicode_GET_SIZE(self.as_ptr()) as usize; - std::slice::from_raw_buf(std::mem::copy_lifetime(self, &buffer), length) + std::slice::from_raw_parts(buffer, length) } } } @@ -46,8 +45,8 @@ impl <'p> ToPyObject<'p> for str { type ObjectType = PyObject<'p>; fn to_py_object(&self, py : Python<'p>) -> PyResult<'p, PyObject<'p>> { - let ptr : *const c_char = self.as_ptr() as *const _; - let len : ffi::Py_ssize_t = std::num::from_uint(self.len()).unwrap(); + let ptr = self.as_ptr() as *const c_char; + let len = self.len() as ffi::Py_ssize_t; unsafe { let obj = if self.is_ascii() { ffi::PyString_FromStringAndSize(ptr, len) @@ -71,8 +70,8 @@ fn u32_as_bytes(input: &[u32]) -> &[u8] { unsafe { std::mem::transmute(input) } } -impl <'p, 's> FromPyObject<'p, 's> for CowString<'s> { - fn from_py_object(o: &'s PyObject<'p>) -> PyResult<'p, CowString<'s>> { +impl <'p, 's> FromPyObject<'p, 's> for Cow<'s, str> { + fn from_py_object(o: &'s PyObject<'p>) -> PyResult<'p, Cow<'s, str>> { let py = o.python(); if let Ok(s) = o.cast_as::() { match s.as_str() { @@ -101,19 +100,18 @@ impl <'p, 's> FromPyObject<'p, 's> for CowString<'s> { impl <'p, 's> FromPyObject<'p, 's> for String { fn from_py_object(o: &'s PyObject<'p>) -> PyResult<'p, String> { - Ok(try!(o.extract::()).into_owned()) + Ok(try!(o.extract::>()).into_owned()) } } -pub fn string_as_slice<'a, 'p>(s: &'a PyObject<'p>) -> PyResult<'p, &'a [u8]> { +fn string_as_slice<'a, 'p>(s: &'a PyObject<'p>) -> PyResult<'p, &'a [u8]> { unsafe { let mut buffer : *mut c_char = std::mem::uninitialized(); let mut length : ffi::Py_ssize_t = std::mem::uninitialized(); if ffi::PyString_AsStringAndSize(s.as_ptr(), &mut buffer, &mut length) == 1 { Err(PyErr::fetch(s.python())) } else { - let buffer = buffer as *const u8; // TODO see std::ffi - Ok(std::slice::from_raw_buf(std::mem::copy_lifetime(s, &buffer), length as usize)) + Ok(std::slice::from_raw_parts(buffer as *const u8, length as usize)) } } } @@ -125,6 +123,6 @@ fn test_non_bmp() { let py = gil.python(); let s = "\u{1F30F}"; let py_string = s.to_py_object(py).unwrap(); - assert_eq!(s, py_string.extract::().unwrap()); + assert_eq!(s, py_string.extract::>().unwrap()); } diff --git a/src/python.rs b/src/python.rs index bd96855c..5559fbab 100644 --- a/src/python.rs +++ b/src/python.rs @@ -1,11 +1,15 @@ use std; -use std::marker::{InvariantLifetime}; +use std::marker::PhantomData; use std::ptr; use ffi; use objects::{PyObject, PyType, PyBool, PyModule}; use err::PyResult; use pythonrun::GILGuard; -use cstr::CStr; +use std::ffi::CStr; + +// Dummy struct representing the global state in the python interpreter. +struct PythonInterpreterState; +impl !Sync for PythonInterpreterState {} /// The 'Python' struct is a zero-size marker struct that is required for most python operations. /// This is used to indicate that the operation accesses/modifies the python interpreter state, @@ -15,9 +19,7 @@ use cstr::CStr; /// You can imagine the GIL to be a giant "Mutex". This makes 'p the lifetime of the /// python state protected by that mutex. #[derive(Copy)] -pub struct Python<'p>(InvariantLifetime<'p>); - -impl <'p> !Send for Python<'p> {} +pub struct Python<'p>(PhantomData<&'p PythonInterpreterState>); // Trait for converting from Self to *mut ffi::PyObject pub trait ToPythonPointer { @@ -108,7 +110,7 @@ impl<'p> Python<'p> { /// and stays acquired for the lifetime 'p #[inline] pub unsafe fn assume_gil_acquired() -> Python<'p> { - Python(InvariantLifetime) + Python(PhantomData) } /// Acquires the global interpreter lock, which allows access to the Python runtime.