Implement 128bit integer conversion for limited API
This commit is contained in:
parent
9aa70f7c89
commit
8c4cba25b7
|
@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Add FFI definitions for `PyBuffer_SizeFromFormat`, `PyObject_LengthHint`, `PyObject_CallNoArgs`, `PyObject_CallOneArg`, `PyObject_CallMethodNoArgs`, `PyObject_CallMethodOneArg`, `PyObject_VectorcallDict`, and `PyObject_VectorcallMethod`. [#1287](https://github.com/PyO3/pyo3/pull/1287)
|
- Add FFI definitions for `PyBuffer_SizeFromFormat`, `PyObject_LengthHint`, `PyObject_CallNoArgs`, `PyObject_CallOneArg`, `PyObject_CallMethodNoArgs`, `PyObject_CallMethodOneArg`, `PyObject_VectorcallDict`, and `PyObject_VectorcallMethod`. [#1287](https://github.com/PyO3/pyo3/pull/1287)
|
||||||
- Allow the use of a string literal in `#[pyclass(name = "string literal")]`. [#1295](https://github.com/PyO3/pyo3/pull/1295)
|
- Allow the use of a string literal in `#[pyclass(name = "string literal")]`. [#1295](https://github.com/PyO3/pyo3/pull/1295)
|
||||||
- Add section about Python::check_signals to the FAQ page of the user guide. [#1301](https://github.com/PyO3/pyo3/pull/1301)
|
- Add section about Python::check_signals to the FAQ page of the user guide. [#1301](https://github.com/PyO3/pyo3/pull/1301)
|
||||||
|
- Add conversions between u128/i128 and PyLong for abi3 and PyPy. [#1310](https://github.com/PyO3/pyo3/pull/1310)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Change return type `PyType::name()` from `Cow<str>` to `PyResult<&str>`. [#1152](https://github.com/PyO3/pyo3/pull/1152)
|
- Change return type `PyType::name()` from `Cow<str>` to `PyResult<&str>`. [#1152](https://github.com/PyO3/pyo3/pull/1152)
|
||||||
|
|
156
src/types/num.rs
156
src/types/num.rs
|
@ -157,23 +157,13 @@ int_convert_u64_or_i64!(
|
||||||
ffi::PyLong_AsUnsignedLongLong
|
ffi::PyLong_AsUnsignedLongLong
|
||||||
);
|
);
|
||||||
|
|
||||||
// manual implementation for 128bit integers
|
|
||||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||||
mod int128_conversion {
|
mod fast_128bit_int_conversion {
|
||||||
use crate::{
|
use super::*;
|
||||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult,
|
|
||||||
Python, ToPyObject,
|
|
||||||
};
|
|
||||||
use std::os::raw::{c_int, c_uchar};
|
|
||||||
|
|
||||||
#[cfg(target_endian = "little")]
|
|
||||||
const IS_LITTLE_ENDIAN: c_int = 1;
|
|
||||||
#[cfg(not(target_endian = "little"))]
|
|
||||||
const IS_LITTLE_ENDIAN: c_int = 0;
|
|
||||||
|
|
||||||
// for 128bit Integers
|
// for 128bit Integers
|
||||||
macro_rules! int_convert_128 {
|
macro_rules! int_convert_128 {
|
||||||
($rust_type: ty, $byte_size: expr, $is_signed: expr) => {
|
($rust_type: ty, $is_signed: expr) => {
|
||||||
impl ToPyObject for $rust_type {
|
impl ToPyObject for $rust_type {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_object(&self, py: Python) -> PyObject {
|
fn to_object(&self, py: Python) -> PyObject {
|
||||||
|
@ -183,11 +173,12 @@ mod int128_conversion {
|
||||||
impl IntoPy<PyObject> for $rust_type {
|
impl IntoPy<PyObject> for $rust_type {
|
||||||
fn into_py(self, py: Python) -> PyObject {
|
fn into_py(self, py: Python) -> PyObject {
|
||||||
unsafe {
|
unsafe {
|
||||||
let bytes = self.to_ne_bytes();
|
// Always use little endian
|
||||||
|
let bytes = self.to_le_bytes();
|
||||||
let obj = ffi::_PyLong_FromByteArray(
|
let obj = ffi::_PyLong_FromByteArray(
|
||||||
bytes.as_ptr() as *const c_uchar,
|
bytes.as_ptr() as *const std::os::raw::c_uchar,
|
||||||
$byte_size,
|
bytes.len(),
|
||||||
IS_LITTLE_ENDIAN,
|
1,
|
||||||
$is_signed,
|
$is_signed,
|
||||||
);
|
);
|
||||||
PyObject::from_owned_ptr(py, obj)
|
PyObject::from_owned_ptr(py, obj)
|
||||||
|
@ -202,18 +193,18 @@ mod int128_conversion {
|
||||||
if num.is_null() {
|
if num.is_null() {
|
||||||
return Err(PyErr::fetch(ob.py()));
|
return Err(PyErr::fetch(ob.py()));
|
||||||
}
|
}
|
||||||
let mut buffer = [0; $byte_size];
|
let mut buffer = [0; std::mem::size_of::<$rust_type>()];
|
||||||
let ok = ffi::_PyLong_AsByteArray(
|
let ok = ffi::_PyLong_AsByteArray(
|
||||||
num as *mut ffi::PyLongObject,
|
num as *mut ffi::PyLongObject,
|
||||||
buffer.as_mut_ptr(),
|
buffer.as_mut_ptr(),
|
||||||
$byte_size,
|
buffer.len(),
|
||||||
IS_LITTLE_ENDIAN,
|
1,
|
||||||
$is_signed,
|
$is_signed,
|
||||||
);
|
);
|
||||||
if ok == -1 {
|
if ok == -1 {
|
||||||
Err(PyErr::fetch(ob.py()))
|
Err(PyErr::fetch(ob.py()))
|
||||||
} else {
|
} else {
|
||||||
Ok(<$rust_type>::from_ne_bytes(buffer))
|
Ok(<$rust_type>::from_le_bytes(buffer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,64 +212,121 @@ mod int128_conversion {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
int_convert_128!(i128, 16, 1);
|
int_convert_128!(i128, 1);
|
||||||
int_convert_128!(u128, 16, 0);
|
int_convert_128!(u128, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
// For ABI3 and PyPy, we implement the conversion using `call_method`.
|
||||||
mod test {
|
#[cfg(any(Py_LIMITED_API, PyPy))]
|
||||||
use crate::{Python, ToPyObject};
|
mod slow_128bit_int_conversion {
|
||||||
|
use super::*;
|
||||||
|
use crate::types::{IntoPyDict, PyBytes, PyDict, PyType};
|
||||||
|
|
||||||
#[test]
|
// Returns `{'signed': True}` if is_signed else None
|
||||||
fn test_i128_max() {
|
fn kwargs(py: Python, is_signed: bool) -> Option<&PyDict> {
|
||||||
let gil = Python::acquire_gil();
|
if is_signed {
|
||||||
let py = gil.python();
|
Some([("signed", is_signed)].into_py_dict(py))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for 128bit Integers
|
||||||
|
macro_rules! int_convert_128 {
|
||||||
|
($rust_type: ty, $is_signed: literal) => {
|
||||||
|
impl ToPyObject for $rust_type {
|
||||||
|
#[inline]
|
||||||
|
fn to_object(&self, py: Python) -> PyObject {
|
||||||
|
(*self).into_py(py)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoPy<PyObject> for $rust_type {
|
||||||
|
fn into_py(self, py: Python) -> PyObject {
|
||||||
|
let bytes = PyBytes::new(py, &self.to_le_bytes());
|
||||||
|
let longtype = unsafe { PyType::from_type_ptr(py, &mut ffi::PyLong_Type) };
|
||||||
|
longtype
|
||||||
|
.call_method("from_bytes", (bytes, "little"), kwargs(py, $is_signed))
|
||||||
|
.expect("Integer conversion (u128/i128 to PyLong) failed")
|
||||||
|
.into_py(py)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'source> FromPyObject<'source> for $rust_type {
|
||||||
|
fn extract(ob: &'source PyAny) -> PyResult<$rust_type> {
|
||||||
|
let py = ob.py();
|
||||||
|
let num: &PyLong = unsafe {
|
||||||
|
crate::FromPyPointer::from_owned_ptr_or_err(
|
||||||
|
py,
|
||||||
|
ffi::PyNumber_Index(ob.as_ptr()),
|
||||||
|
)
|
||||||
|
}?;
|
||||||
|
let mut bytes = [0u8; std::mem::size_of::<$rust_type>()];
|
||||||
|
let pybytes: &PyBytes = num
|
||||||
|
.call_method("to_bytes", (bytes.len(), "little"), kwargs(py, $is_signed))?
|
||||||
|
.extract()?;
|
||||||
|
bytes.copy_from_slice(pybytes.as_bytes());
|
||||||
|
Ok(<$rust_type>::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int_convert_128!(i128, true);
|
||||||
|
int_convert_128!(u128, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_128bit_intergers {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_i128_max() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
let v = std::i128::MAX;
|
let v = std::i128::MAX;
|
||||||
let obj = v.to_object(py);
|
let obj = v.to_object(py);
|
||||||
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
||||||
assert_eq!(v as u128, obj.extract::<u128>(py).unwrap());
|
assert_eq!(v as u128, obj.extract::<u128>(py).unwrap());
|
||||||
assert!(obj.extract::<u64>(py).is_err());
|
assert!(obj.extract::<u64>(py).is_err());
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_i128_min() {
|
fn test_i128_min() {
|
||||||
let gil = Python::acquire_gil();
|
Python::with_gil(|py| {
|
||||||
let py = gil.python();
|
|
||||||
let v = std::i128::MIN;
|
let v = std::i128::MIN;
|
||||||
let obj = v.to_object(py);
|
let obj = v.to_object(py);
|
||||||
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
||||||
assert!(obj.extract::<i64>(py).is_err());
|
assert!(obj.extract::<i64>(py).is_err());
|
||||||
assert!(obj.extract::<u128>(py).is_err());
|
assert!(obj.extract::<u128>(py).is_err());
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_u128_max() {
|
fn test_u128_max() {
|
||||||
let gil = Python::acquire_gil();
|
Python::with_gil(|py| {
|
||||||
let py = gil.python();
|
|
||||||
let v = std::u128::MAX;
|
let v = std::u128::MAX;
|
||||||
let obj = v.to_object(py);
|
let obj = v.to_object(py);
|
||||||
assert_eq!(v, obj.extract::<u128>(py).unwrap());
|
assert_eq!(v, obj.extract::<u128>(py).unwrap());
|
||||||
assert!(obj.extract::<i128>(py).is_err());
|
assert!(obj.extract::<i128>(py).is_err());
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_u128_overflow() {
|
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||||
use crate::{exceptions, ffi, PyObject};
|
fn test_u128_overflow() {
|
||||||
use std::os::raw::c_uchar;
|
use crate::{exceptions, ffi, PyObject};
|
||||||
let gil = Python::acquire_gil();
|
use std::os::raw::c_uchar;
|
||||||
let py = gil.python();
|
Python::with_gil(|py| {
|
||||||
let overflow_bytes: [c_uchar; 20] = [255; 20];
|
let overflow_bytes: [c_uchar; 20] = [255; 20];
|
||||||
unsafe {
|
unsafe {
|
||||||
let obj = ffi::_PyLong_FromByteArray(
|
let obj =
|
||||||
overflow_bytes.as_ptr() as *const c_uchar,
|
ffi::_PyLong_FromByteArray(overflow_bytes.as_ptr() as *const c_uchar, 20, 1, 0);
|
||||||
20,
|
|
||||||
super::IS_LITTLE_ENDIAN,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
let obj = PyObject::from_owned_ptr(py, obj);
|
let obj = PyObject::from_owned_ptr(py, obj);
|
||||||
let err = obj.extract::<u128>(py).unwrap_err();
|
let err = obj.extract::<u128>(py).unwrap_err();
|
||||||
assert!(err.is_instance::<exceptions::PyOverflowError>(py));
|
assert!(err.is_instance::<exceptions::PyOverflowError>(py));
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue