PyTryFrom::try_from_unchecked()

This allows casting a PyObjectRef to a specific type of PyObject
with no overhead.
This commit is contained in:
ijl 2018-12-01 10:12:27 -05:00
parent 790a103753
commit d18ebea1c2
2 changed files with 51 additions and 14 deletions

View File

@ -233,6 +233,10 @@ pub trait PyTryFrom: Sized {
/// Cast from a concrete Python object type to PyObject. With exact type check.
fn try_from_mut_exact(value: &PyObjectRef) -> Result<&mut Self, PyDowncastError>;
/// Cast a PyObjectRef to a specific type of PyObject. The caller must
/// have already verified the reference is for this type.
unsafe fn try_from_unchecked(value: &PyObjectRef) -> &Self;
}
// TryFrom implies TryInto
@ -263,12 +267,7 @@ where
fn try_from(value: &PyObjectRef) -> Result<&T, PyDowncastError> {
unsafe {
if T::is_instance(value) {
let ptr = if T::OFFSET == 0 {
value as *const _ as *mut u8 as *mut T
} else {
(value.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T
};
Ok(&*ptr)
Ok(PyTryFrom::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
@ -278,12 +277,7 @@ where
fn try_from_exact(value: &PyObjectRef) -> Result<&T, PyDowncastError> {
unsafe {
if T::is_exact_instance(value) {
let ptr = if T::OFFSET == 0 {
value as *const _ as *mut u8 as *mut T
} else {
(value.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T
};
Ok(&*ptr)
Ok(PyTryFrom::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
@ -319,6 +313,16 @@ where
}
}
}
#[inline]
unsafe fn try_from_unchecked(value: &PyObjectRef) -> &T {
let ptr = if T::OFFSET == 0 {
value as *const _ as *const u8 as *const T
} else {
(value.as_ptr() as *const u8).offset(T::OFFSET) as *const T
};
&*ptr
}
}
/// This trait wraps a T: IntoPyObject into PyResult<T> while PyResult<T> remains PyResult<T>.
@ -347,3 +351,19 @@ impl<T: IntoPyObject> ReturnTypeIntoPyResult for PyResult<T> {
self
}
}
#[cfg(test)]
mod test {
use crate::Python;
use crate::types::PyList;
use super::PyTryFrom;
#[test]
fn test_try_from_unchecked() {
let gil = Python::acquire_gil();
let py = gil.python();
let list = PyList::new(py, &[1, 2, 3]);
let val = unsafe { <PyList as PyTryFrom>::try_from_unchecked(list.as_ref()) };
assert_eq!(list, val);
}
}

View File

@ -279,8 +279,7 @@ impl PyTryFrom for PySequence {
fn try_from(value: &PyObjectRef) -> Result<&PySequence, PyDowncastError> {
unsafe {
if ffi::PySequence_Check(value.as_ptr()) != 0 {
let ptr = value as *const _ as *mut PySequence;
Ok(&*ptr)
Ok(<PySequence as PyTryFrom>::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
@ -305,6 +304,12 @@ impl PyTryFrom for PySequence {
fn try_from_mut_exact(value: &PyObjectRef) -> Result<&mut PySequence, PyDowncastError> {
PySequence::try_from_mut(value)
}
#[inline]
unsafe fn try_from_unchecked(value: &PyObjectRef) -> &PySequence {
let ptr = value as *const _ as *const PySequence;
&*ptr
}
}
#[cfg(test)]
@ -629,4 +634,16 @@ mod test {
.unwrap();
assert!(v == b"abc");
}
#[test]
fn test_seq_try_from_unchecked() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = vec!["foo", "bar"];
let ob = v.to_object(py);
let seq = ob.cast_as::<PySequence>(py).unwrap();
let type_ptr = seq.as_ref();
let seq_from = unsafe { <PySequence as PyTryFrom>::try_from_unchecked(type_ptr) };
assert!(seq_from.list().is_ok());
}
}