From a87ddeab215c38b0a0d84f361dce44fdb3a70131 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sat, 2 Jun 2018 21:34:35 +0900 Subject: [PATCH 1/4] Add 128bit integer support for python3 --- src/ffi3/longobject.rs | 21 ++++++- src/objects/num3.rs | 123 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 142 insertions(+), 2 deletions(-) diff --git a/src/ffi3/longobject.rs b/src/ffi3/longobject.rs index 5c8ef0153ef..92311532f7a 100644 --- a/src/ffi3/longobject.rs +++ b/src/ffi3/longobject.rs @@ -1,5 +1,7 @@ use libc::size_t; -use std::os::raw::{c_void, c_char, c_int, c_long, c_ulong, c_longlong, c_ulonglong, c_double}; +use std::os::raw::{ + c_void, c_char, c_int, c_long, c_ulong, c_longlong, c_ulonglong, c_double, c_uchar +}; use ffi3::object::*; use ffi3::pyport::Py_ssize_t; @@ -59,3 +61,20 @@ pub unsafe fn PyLong_CheckExact(op : *mut PyObject) -> c_int { -> c_long; } +#[cfg(not(Py_LIMITED_API))] +#[cfg_attr(windows, link(name = "pythonXY"))] +extern "C" { + pub fn _PyLong_FromByteArray( + arg1: *const c_uchar, + arg2: size_t, + arg3: c_int, // is_little_endian + arg4: c_int, // is_signed + ) -> *mut PyObject; + pub fn _PyLong_AsByteArray( + arg1: *mut PyLongObject, + arg2: *const c_uchar, + arg3: size_t, + arg4: c_int, // is_little_endian + arg5: c_int, // is_signed + ) -> c_int; +} diff --git a/src/objects/num3.rs b/src/objects/num3.rs index 21119c00fe1..54987a59ee4 100644 --- a/src/objects/num3.rs +++ b/src/objects/num3.rs @@ -2,7 +2,7 @@ // // based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython -use std::os::raw::c_long; +use std::os::raw::{c_long, c_uchar, c_int}; extern crate num_traits; use self::num_traits::cast::cast; @@ -138,6 +138,55 @@ macro_rules! int_convert_u64_or_i64 ( ) ); +// for 128bit Integers +macro_rules! int_convert_bignum ( + ($rust_type: ty, $byte_size: expr, $is_little_endian: expr, $is_signed: expr) => ( + impl ToPyObject for $rust_type { + #[inline] + fn to_object(&self, py: Python) -> PyObject { + self.into_object(py) + } + } + impl IntoPyObject for $rust_type { + fn into_object(self, py: Python) -> PyObject { + unsafe { + let bytes = ::std::mem::transmute::<_, [c_uchar; $byte_size]>(self); + let obj = ffi::_PyLong_FromByteArray( + bytes.as_ptr() as *const c_uchar, + $byte_size, + $is_little_endian, + $is_signed, + ); + PyObject::from_owned_ptr_or_panic(py, obj) + } + } + } + impl<'source> FromPyObject<'source> for $rust_type { + #[cfg(target_endian = "little")] + fn extract(ob: &'source PyObjectRef) -> PyResult<$rust_type> { + unsafe { + let num = ffi::PyNumber_Index(ob.as_ptr()); + if num.is_null() { + return Err(PyErr::fetch(ob.py())); + } + let buffer: [c_uchar; $byte_size] = [0; $byte_size]; + let ok = ffi::_PyLong_AsByteArray( + ob.as_ptr() as *mut ffi::PyLongObject, + buffer.as_ptr() as *const c_uchar, + $byte_size, + $is_little_endian, + $is_signed, + ); + if ok == -1 { + Err(PyErr::fetch(ob.py())) + } else { + Ok(::std::mem::transmute::<_, $rust_type>(buffer)) + } + } + } + } + ) +); int_fits_c_long!(i8); int_fits_c_long!(u8); @@ -168,6 +217,16 @@ int_fits_larger_int!(usize, u64); // u64 has a manual implementation as it never fits into signed long int_convert_u64_or_i64!(u64, ffi::PyLong_FromUnsignedLongLong, ffi::PyLong_AsUnsignedLongLong); +// manual implementation for 128bit integers +#[cfg(target_endian = "little")] +const IS_LITTLE_ENDIAN: c_int = 1; +#[cfg(not(target_endian = "little"))] +const IS_LITTLE_ENDIAN: c_int = 0; + + #[cfg(not(Py_LIMITED_API))] +int_convert_bignum!(i128, 16, IS_LITTLE_ENDIAN, 1); + #[cfg(not(Py_LIMITED_API))] +int_convert_bignum!(u128, 16, IS_LITTLE_ENDIAN, 0); #[cfg(test)] mod test { @@ -224,6 +283,10 @@ mod test { test_common!(u64, u64); test_common!(isize, isize); test_common!(usize, usize); + #[cfg(not(Py_LIMITED_API))] + test_common!(i128, i128); + #[cfg(not(Py_LIMITED_API))] + test_common!(u128, u128); #[test] fn test_u32_max() { @@ -267,4 +330,62 @@ mod test { assert_eq!(v, obj.extract::(py).unwrap()); assert!(obj.extract::(py).is_err()); } + + #[test] + #[cfg(not(Py_LIMITED_API))] + fn test_i128_max() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::i128::MAX; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert_eq!(v as u128, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + #[cfg(not(Py_LIMITED_API))] + fn test_i128_min() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::i128::MIN; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + #[cfg(not(Py_LIMITED_API))] + fn test_u128_max() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::u128::MAX; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + #[cfg(not(Py_LIMITED_API))] + fn test_u128_overflow() { + use ffi; + use object::PyObject; + use objects::exc; + use std::os::raw::c_uchar; + let gil = Python::acquire_gil(); + let py = gil.python(); + let overflow_bytes: [c_uchar; 20] = [255; 20]; + unsafe { + let obj = ffi::_PyLong_FromByteArray( + overflow_bytes.as_ptr() as *const c_uchar, + 20, + super::IS_LITTLE_ENDIAN, + 0, + ); + let obj = PyObject::from_owned_ptr_or_panic(py, obj); + let err = obj.extract::(py).unwrap_err(); + assert!(err.is_instance::(py)); + } + } } From 9ffbae30abfc2ca06cefc5bad38765d49bc8471e Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sat, 2 Jun 2018 22:10:13 +0900 Subject: [PATCH 2/4] Add 128bit integer support for python2 --- src/ffi2/longobject.rs | 42 +++++++---------- src/objects/num2.rs | 104 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 26 deletions(-) diff --git a/src/ffi2/longobject.rs b/src/ffi2/longobject.rs index 3d13c58b94a..bb4348ec344 100644 --- a/src/ffi2/longobject.rs +++ b/src/ffi2/longobject.rs @@ -1,10 +1,11 @@ -use std::os::raw::{c_void, c_char, c_int, c_long, c_ulong, c_longlong, c_ulonglong, c_double}; +use std::os::raw::{ + c_void, c_char, c_int, c_long, c_ulong, c_longlong, c_ulonglong, c_double, c_uchar +}; use libc::size_t; use ffi2::pyport::Py_ssize_t; use ffi2::object::*; -//enum PyLongObject { /* representation hidden */ } - +pub enum PyLongObject {} #[cfg_attr(windows, link(name="pythonXY"))] extern "C" { pub static mut PyLong_Type: PyTypeObject; @@ -58,26 +59,17 @@ pub unsafe fn PyLong_CheckExact(op : *mut PyObject) -> c_int { pub fn PyLong_GetInfo() -> *mut PyObject; - /* - pub fn _PyLong_AsInt(arg1: *mut PyObject) -> c_int; - pub fn _PyLong_Frexp(a: *mut PyLongObject, e: *mut Py_ssize_t) - -> c_double; - - pub fn _PyLong_Sign(v: *mut PyObject) -> c_int; - pub fn _PyLong_NumBits(v: *mut PyObject) -> size_t; - pub fn _PyLong_FromByteArray(bytes: *const c_uchar, n: size_t, - little_endian: c_int, - is_signed: c_int) -> *mut PyObject; - pub fn _PyLong_AsByteArray(v: *mut PyLongObject, - bytes: *mut c_uchar, n: size_t, - little_endian: c_int, - is_signed: c_int) -> c_int; - pub fn _PyLong_Format(aa: *mut PyObject, base: c_int, - addL: c_int, newstyle: c_int) - -> *mut PyObject; - pub fn _PyLong_FormatAdvanced(obj: *mut PyObject, - format_spec: *mut c_char, - format_spec_len: Py_ssize_t) - -> *mut PyObject;*/ + pub fn _PyLong_FromByteArray( + arg1: *const c_uchar, + arg2: size_t, + arg3: c_int, // is_little_endian + arg4: c_int, // is_signed + ) -> *mut PyObject; + pub fn _PyLong_AsByteArray( + arg1: *mut PyLongObject, + arg2: *const c_uchar, + arg3: size_t, + arg4: c_int, // is_little_endian + arg5: c_int, // is_signed + ) -> c_int; } - diff --git a/src/objects/num2.rs b/src/objects/num2.rs index b32ea284bf8..0d69ec3d85c 100644 --- a/src/objects/num2.rs +++ b/src/objects/num2.rs @@ -2,7 +2,7 @@ // // based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython -use std::os::raw::c_long; +use std::os::raw::{c_long, c_uchar, c_int}; extern crate num_traits; use self::num_traits::cast::cast; @@ -174,6 +174,56 @@ macro_rules! int_convert_u64_or_i64 ( ) ); +// for 128bit Integers +macro_rules! int_convert_bignum ( + ($rust_type: ty, $byte_size: expr, $is_little_endian: expr, $is_signed: expr) => ( + impl ToPyObject for $rust_type { + #[inline] + fn to_object(&self, py: Python) -> PyObject { + self.into_object(py) + } + } + impl IntoPyObject for $rust_type { + fn into_object(self, py: Python) -> PyObject { + unsafe { + let bytes = ::std::mem::transmute::<_, [c_uchar; $byte_size]>(self); + let obj = ffi::_PyLong_FromByteArray( + bytes.as_ptr() as *const c_uchar, + $byte_size, + $is_little_endian, + $is_signed, + ); + PyObject::from_owned_ptr_or_panic(py, obj) + } + } + } + impl<'source> FromPyObject<'source> for $rust_type { + #[cfg(target_endian = "little")] + fn extract(ob: &'source PyObjectRef) -> PyResult<$rust_type> { + unsafe { + let num = ffi::PyNumber_Index(ob.as_ptr()); + if num.is_null() { + return Err(PyErr::fetch(ob.py())); + } + let buffer: [c_uchar; $byte_size] = [0; $byte_size]; + let ok = ffi::_PyLong_AsByteArray( + ob.as_ptr() as *mut ffi::PyLongObject, + buffer.as_ptr() as *const c_uchar, + $byte_size, + $is_little_endian, + $is_signed, + ); + if ok == -1 { + Err(PyErr::fetch(ob.py())) + } else { + Ok(::std::mem::transmute::<_, $rust_type>(buffer)) + } + } + } + } + ) +); + int_fits_c_long!(i8); int_fits_c_long!(u8); @@ -204,6 +254,13 @@ int_fits_larger_int!(usize, u64); // u64 has a manual implementation as it never fits into signed long int_convert_u64_or_i64!(u64, ffi::PyLong_FromUnsignedLongLong, ffi::PyLong_AsUnsignedLongLong); +// manual implementation for 128bit integers +#[cfg(target_endian = "little")] +const IS_LITTLE_ENDIAN: c_int = 1; +#[cfg(not(target_endian = "little"))] +const IS_LITTLE_ENDIAN: c_int = 0; +int_convert_bignum!(i128, 16, IS_LITTLE_ENDIAN, 1); +int_convert_bignum!(u128, 16, IS_LITTLE_ENDIAN, 0); #[cfg(test)] mod test { @@ -236,6 +293,8 @@ mod test { num_to_py_object_and_back!(to_from_u64, u64, u64); num_to_py_object_and_back!(to_from_isize, isize, isize); num_to_py_object_and_back!(to_from_usize, usize, usize); + num_to_py_object_and_back!(to_from_i128, i128, i128); + num_to_py_object_and_back!(to_from_u128, u128, u128); num_to_py_object_and_back!(float_to_i32, f64, i32); num_to_py_object_and_back!(float_to_u32, f64, u32); num_to_py_object_and_back!(float_to_i64, f64, i64); @@ -284,4 +343,47 @@ mod test { assert_eq!(v, obj.extract::(py).unwrap()); assert!(obj.extract::(py).is_err()); } + + #[test] + fn test_i128_min() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::i128::MIN; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + fn test_u128_max() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::u128::MAX; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + fn test_u128_overflow() { + use ffi; + use object::PyObject; + use objects::exc; + use std::os::raw::c_uchar; + let gil = Python::acquire_gil(); + let py = gil.python(); + let overflow_bytes: [c_uchar; 20] = [255; 20]; + unsafe { + let obj = ffi::_PyLong_FromByteArray( + overflow_bytes.as_ptr() as *const c_uchar, + 20, + super::IS_LITTLE_ENDIAN, + 0, + ); + let obj = PyObject::from_owned_ptr_or_panic(py, obj); + let err = obj.extract::(py).unwrap_err(); + assert!(err.is_instance::(py)); + } + } } From 0b964b503f41c6a04c613f9f7043b4d07048bc76 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 5 Jun 2018 23:03:53 +0900 Subject: [PATCH 3/4] Rename args of _PyLong_FromByteArray/_PyLong_AsByteArray --- src/ffi2/longobject.rs | 19 ++++++++++--------- src/ffi3/longobject.rs | 19 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/ffi2/longobject.rs b/src/ffi2/longobject.rs index bb4348ec344..54890aa5639 100644 --- a/src/ffi2/longobject.rs +++ b/src/ffi2/longobject.rs @@ -60,16 +60,17 @@ pub unsafe fn PyLong_CheckExact(op : *mut PyObject) -> c_int { pub fn PyLong_GetInfo() -> *mut PyObject; pub fn _PyLong_FromByteArray( - arg1: *const c_uchar, - arg2: size_t, - arg3: c_int, // is_little_endian - arg4: c_int, // is_signed + bytes: *const c_uchar, + n: size_t, + little_endian: c_int, + is_signed: c_int, ) -> *mut PyObject; + pub fn _PyLong_AsByteArray( - arg1: *mut PyLongObject, - arg2: *const c_uchar, - arg3: size_t, - arg4: c_int, // is_little_endian - arg5: c_int, // is_signed + v: *mut PyLongObject, + bytes: *const c_uchar, + n: size_t, + little_endian: c_int, + is_signed: c_int, ) -> c_int; } diff --git a/src/ffi3/longobject.rs b/src/ffi3/longobject.rs index 92311532f7a..30397a9af59 100644 --- a/src/ffi3/longobject.rs +++ b/src/ffi3/longobject.rs @@ -65,16 +65,17 @@ pub unsafe fn PyLong_CheckExact(op : *mut PyObject) -> c_int { #[cfg_attr(windows, link(name = "pythonXY"))] extern "C" { pub fn _PyLong_FromByteArray( - arg1: *const c_uchar, - arg2: size_t, - arg3: c_int, // is_little_endian - arg4: c_int, // is_signed + bytes: *const c_uchar, + n: size_t, + little_endian: c_int, + is_signed: c_int, ) -> *mut PyObject; + pub fn _PyLong_AsByteArray( - arg1: *mut PyLongObject, - arg2: *const c_uchar, - arg3: size_t, - arg4: c_int, // is_little_endian - arg5: c_int, // is_signed + v: *mut PyLongObject, + bytes: *const c_uchar, + n: size_t, + little_endian: c_int, + is_signed: c_int, ) -> c_int; } From 66183d4683f783c11a5d95d2f81d0290c415549d Mon Sep 17 00:00:00 2001 From: kngwyu Date: Wed, 6 Jun 2018 01:31:40 +0900 Subject: [PATCH 4/4] Move common functions and macros from num2&num3 to num_common --- src/objects/mod.rs | 3 + src/objects/num2.rs | 181 +-------------------------------- src/objects/num3.rs | 200 +------------------------------------ src/objects/num_common.rs | 205 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 212 insertions(+), 377 deletions(-) create mode 100644 src/objects/num_common.rs diff --git a/src/objects/mod.rs b/src/objects/mod.rs index ae8ecf7c710..9ffe3c55834 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -230,6 +230,9 @@ mod stringutils; mod set; pub mod exc; +#[macro_use] +mod num_common; + #[cfg(Py_3)] mod num3; diff --git a/src/objects/num2.rs b/src/objects/num2.rs index 0d69ec3d85c..cea2fcef515 100644 --- a/src/objects/num2.rs +++ b/src/objects/num2.rs @@ -2,7 +2,7 @@ // // based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython -use std::os::raw::{c_long, c_uchar, c_int}; +use std::os::raw::{c_long, c_uchar}; extern crate num_traits; use self::num_traits::cast::cast; @@ -14,6 +14,7 @@ use err::{PyResult, PyErr}; use instance::{Py, PyObjectWithToken}; use objects::{exc, PyObjectRef}; use conversion::{ToPyObject, IntoPyObject, FromPyObject}; +use super::num_common::{err_if_invalid_value, IS_LITTLE_ENDIAN}; /// Represents a Python `int` object. /// @@ -90,41 +91,6 @@ macro_rules! int_fits_c_long( ) ); - -macro_rules! int_fits_larger_int( - ($rust_type:ty, $larger_type:ty) => ( - impl ToPyObject for $rust_type { - #[inline] - fn to_object(&self, py: Python) -> PyObject { - (*self as $larger_type).to_object(py) - } - } - impl IntoPyObject for $rust_type { - fn into_object(self, py: Python) -> PyObject { - (self as $larger_type).into_object(py) - } - } - pyobject_extract!(obj to $rust_type => { - let val = try!($crate::ObjectProtocol::extract::<$larger_type>(obj)); - match cast::<$larger_type, $rust_type>(val) { - Some(v) => Ok(v), - None => Err(exc::OverflowError.into()) - } - }); - ) -); - - -fn err_if_invalid_value<'p, T: PartialEq> - (py: Python, invalid_value: T, actual_value: T) -> PyResult -{ - if actual_value == invalid_value && PyErr::occurred(py) { - Err(PyErr::fetch(py)) - } else { - Ok(actual_value) - } -} - macro_rules! int_convert_u64_or_i64 ( ($rust_type:ty, $pylong_from_ll_or_ull:expr, $pylong_as_ull_or_ull:expr) => ( impl ToPyObject for $rust_type { @@ -174,57 +140,6 @@ macro_rules! int_convert_u64_or_i64 ( ) ); -// for 128bit Integers -macro_rules! int_convert_bignum ( - ($rust_type: ty, $byte_size: expr, $is_little_endian: expr, $is_signed: expr) => ( - impl ToPyObject for $rust_type { - #[inline] - fn to_object(&self, py: Python) -> PyObject { - self.into_object(py) - } - } - impl IntoPyObject for $rust_type { - fn into_object(self, py: Python) -> PyObject { - unsafe { - let bytes = ::std::mem::transmute::<_, [c_uchar; $byte_size]>(self); - let obj = ffi::_PyLong_FromByteArray( - bytes.as_ptr() as *const c_uchar, - $byte_size, - $is_little_endian, - $is_signed, - ); - PyObject::from_owned_ptr_or_panic(py, obj) - } - } - } - impl<'source> FromPyObject<'source> for $rust_type { - #[cfg(target_endian = "little")] - fn extract(ob: &'source PyObjectRef) -> PyResult<$rust_type> { - unsafe { - let num = ffi::PyNumber_Index(ob.as_ptr()); - if num.is_null() { - return Err(PyErr::fetch(ob.py())); - } - let buffer: [c_uchar; $byte_size] = [0; $byte_size]; - let ok = ffi::_PyLong_AsByteArray( - ob.as_ptr() as *mut ffi::PyLongObject, - buffer.as_ptr() as *const c_uchar, - $byte_size, - $is_little_endian, - $is_signed, - ); - if ok == -1 { - Err(PyErr::fetch(ob.py())) - } else { - Ok(::std::mem::transmute::<_, $rust_type>(buffer)) - } - } - } - } - ) -); - - int_fits_c_long!(i8); int_fits_c_long!(u8); int_fits_c_long!(i16); @@ -254,17 +169,11 @@ int_fits_larger_int!(usize, u64); // u64 has a manual implementation as it never fits into signed long int_convert_u64_or_i64!(u64, ffi::PyLong_FromUnsignedLongLong, ffi::PyLong_AsUnsignedLongLong); -// manual implementation for 128bit integers -#[cfg(target_endian = "little")] -const IS_LITTLE_ENDIAN: c_int = 1; -#[cfg(not(target_endian = "little"))] -const IS_LITTLE_ENDIAN: c_int = 0; int_convert_bignum!(i128, 16, IS_LITTLE_ENDIAN, 1); int_convert_bignum!(u128, 16, IS_LITTLE_ENDIAN, 0); #[cfg(test)] mod test { - use std; use python::{Python}; use conversion::ToPyObject; @@ -300,90 +209,4 @@ mod test { num_to_py_object_and_back!(float_to_i64, f64, i64); num_to_py_object_and_back!(float_to_u64, f64, u64); num_to_py_object_and_back!(int_to_float, i32, f64); - - #[test] - fn test_u32_max() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::u32::MAX; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert_eq!(v as u64, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - fn test_i64_max() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::i64::MAX; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert_eq!(v as u64, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - fn test_i64_min() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::i64::MIN; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - fn test_u64_max() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::u64::MAX; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - fn test_i128_min() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::i128::MIN; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - fn test_u128_max() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::u128::MAX; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - fn test_u128_overflow() { - use ffi; - use object::PyObject; - use objects::exc; - use std::os::raw::c_uchar; - let gil = Python::acquire_gil(); - let py = gil.python(); - let overflow_bytes: [c_uchar; 20] = [255; 20]; - unsafe { - let obj = ffi::_PyLong_FromByteArray( - overflow_bytes.as_ptr() as *const c_uchar, - 20, - super::IS_LITTLE_ENDIAN, - 0, - ); - let obj = PyObject::from_owned_ptr_or_panic(py, obj); - let err = obj.extract::(py).unwrap_err(); - assert!(err.is_instance::(py)); - } - } } diff --git a/src/objects/num3.rs b/src/objects/num3.rs index 54987a59ee4..5d80482f0a9 100644 --- a/src/objects/num3.rs +++ b/src/objects/num3.rs @@ -2,7 +2,7 @@ // // based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython -use std::os::raw::{c_long, c_uchar, c_int}; +use std::os::raw::{c_long, c_uchar}; extern crate num_traits; use self::num_traits::cast::cast; @@ -14,7 +14,7 @@ use err::{PyResult, PyErr}; use objects::{exc, PyObjectRef}; use instance::PyObjectWithToken; use conversion::{ToPyObject, IntoPyObject, FromPyObject}; - +use super::num_common::{err_if_invalid_value, IS_LITTLE_ENDIAN}; /// Represents a Python `int` object. /// /// You can usually avoid directly working with this type @@ -25,7 +25,6 @@ pub struct PyLong(PyObject); pyobject_native_type!(PyLong, PyLong_Type, PyLong_Check); - macro_rules! int_fits_c_long( ($rust_type:ty) => ( impl ToPyObject for $rust_type { @@ -64,43 +63,6 @@ macro_rules! int_fits_c_long( ) ); - -macro_rules! int_fits_larger_int( - ($rust_type:ty, $larger_type:ty) => ( - impl ToPyObject for $rust_type { - #[inline] - fn to_object(&self, py: Python) -> PyObject { - (*self as $larger_type).into_object(py) - } - } - impl IntoPyObject for $rust_type { - fn into_object(self, py: Python) -> PyObject { - (self as $larger_type).into_object(py) - } - } - pyobject_extract!(obj to $rust_type => { - let val = try!($crate::objectprotocol::ObjectProtocol::extract::<$larger_type>(obj)); - match cast::<$larger_type, $rust_type>(val) { - Some(v) => Ok(v), - None => Err(exc::OverflowError.into()) - } - }); - ) -); - - - -#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] -fn err_if_invalid_value - (py: Python, invalid_value: T, actual_value: T) -> PyResult -{ - if actual_value == invalid_value && PyErr::occurred(py) { - Err(PyErr::fetch(py)) - } else { - Ok(actual_value) - } -} - macro_rules! int_convert_u64_or_i64 ( ($rust_type:ty, $pylong_from_ll_or_ull:expr, $pylong_as_ll_or_ull:expr) => ( impl ToPyObject for $rust_type { @@ -138,56 +100,6 @@ macro_rules! int_convert_u64_or_i64 ( ) ); -// for 128bit Integers -macro_rules! int_convert_bignum ( - ($rust_type: ty, $byte_size: expr, $is_little_endian: expr, $is_signed: expr) => ( - impl ToPyObject for $rust_type { - #[inline] - fn to_object(&self, py: Python) -> PyObject { - self.into_object(py) - } - } - impl IntoPyObject for $rust_type { - fn into_object(self, py: Python) -> PyObject { - unsafe { - let bytes = ::std::mem::transmute::<_, [c_uchar; $byte_size]>(self); - let obj = ffi::_PyLong_FromByteArray( - bytes.as_ptr() as *const c_uchar, - $byte_size, - $is_little_endian, - $is_signed, - ); - PyObject::from_owned_ptr_or_panic(py, obj) - } - } - } - impl<'source> FromPyObject<'source> for $rust_type { - #[cfg(target_endian = "little")] - fn extract(ob: &'source PyObjectRef) -> PyResult<$rust_type> { - unsafe { - let num = ffi::PyNumber_Index(ob.as_ptr()); - if num.is_null() { - return Err(PyErr::fetch(ob.py())); - } - let buffer: [c_uchar; $byte_size] = [0; $byte_size]; - let ok = ffi::_PyLong_AsByteArray( - ob.as_ptr() as *mut ffi::PyLongObject, - buffer.as_ptr() as *const c_uchar, - $byte_size, - $is_little_endian, - $is_signed, - ); - if ok == -1 { - Err(PyErr::fetch(ob.py())) - } else { - Ok(::std::mem::transmute::<_, $rust_type>(buffer)) - } - } - } - } - ) -); - int_fits_c_long!(i8); int_fits_c_long!(u8); int_fits_c_long!(i16); @@ -217,12 +129,6 @@ int_fits_larger_int!(usize, u64); // u64 has a manual implementation as it never fits into signed long int_convert_u64_or_i64!(u64, ffi::PyLong_FromUnsignedLongLong, ffi::PyLong_AsUnsignedLongLong); -// manual implementation for 128bit integers -#[cfg(target_endian = "little")] -const IS_LITTLE_ENDIAN: c_int = 1; -#[cfg(not(target_endian = "little"))] -const IS_LITTLE_ENDIAN: c_int = 0; - #[cfg(not(Py_LIMITED_API))] int_convert_bignum!(i128, 16, IS_LITTLE_ENDIAN, 1); #[cfg(not(Py_LIMITED_API))] @@ -230,7 +136,6 @@ int_convert_bignum!(u128, 16, IS_LITTLE_ENDIAN, 0); #[cfg(test)] mod test { - use std; use python::Python; use conversion::ToPyObject; @@ -287,105 +192,4 @@ mod test { test_common!(i128, i128); #[cfg(not(Py_LIMITED_API))] test_common!(u128, u128); - - #[test] - fn test_u32_max() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::u32::MAX; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert_eq!(v as u64, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - fn test_i64_max() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::i64::MAX; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert_eq!(v as u64, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - fn test_i64_min() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::i64::MIN; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - fn test_u64_max() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::u64::MAX; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - #[cfg(not(Py_LIMITED_API))] - fn test_i128_max() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::i128::MAX; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert_eq!(v as u128, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - #[cfg(not(Py_LIMITED_API))] - fn test_i128_min() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::i128::MIN; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - #[cfg(not(Py_LIMITED_API))] - fn test_u128_max() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let v = std::u128::MAX; - let obj = v.to_object(py); - assert_eq!(v, obj.extract::(py).unwrap()); - assert!(obj.extract::(py).is_err()); - } - - #[test] - #[cfg(not(Py_LIMITED_API))] - fn test_u128_overflow() { - use ffi; - use object::PyObject; - use objects::exc; - use std::os::raw::c_uchar; - let gil = Python::acquire_gil(); - let py = gil.python(); - let overflow_bytes: [c_uchar; 20] = [255; 20]; - unsafe { - let obj = ffi::_PyLong_FromByteArray( - overflow_bytes.as_ptr() as *const c_uchar, - 20, - super::IS_LITTLE_ENDIAN, - 0, - ); - let obj = PyObject::from_owned_ptr_or_panic(py, obj); - let err = obj.extract::(py).unwrap_err(); - assert!(err.is_instance::(py)); - } - } } diff --git a/src/objects/num_common.rs b/src/objects/num_common.rs new file mode 100644 index 00000000000..e75df4d892c --- /dev/null +++ b/src/objects/num_common.rs @@ -0,0 +1,205 @@ +//! common macros for num2.rs and num3.rs + +use std::os::raw::c_int; +use python::Python; +use err::{PyErr, PyResult}; + +#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] +pub(super) fn err_if_invalid_value + (py: Python, invalid_value: T, actual_value: T) -> PyResult +{ + if actual_value == invalid_value && PyErr::occurred(py) { + Err(PyErr::fetch(py)) + } else { + Ok(actual_value) + } +} + +#[macro_export] +macro_rules! int_fits_larger_int( + ($rust_type:ty, $larger_type:ty) => ( + impl ToPyObject for $rust_type { + #[inline] + fn to_object(&self, py: Python) -> PyObject { + (*self as $larger_type).into_object(py) + } + } + impl IntoPyObject for $rust_type { + fn into_object(self, py: Python) -> PyObject { + (self as $larger_type).into_object(py) + } + } + pyobject_extract!(obj to $rust_type => { + let val = try!($crate::objectprotocol::ObjectProtocol::extract::<$larger_type>(obj)); + match cast::<$larger_type, $rust_type>(val) { + Some(v) => Ok(v), + None => Err(exc::OverflowError.into()) + } + }); + ) +); + + +// for 128bit Integers +#[macro_export] +macro_rules! int_convert_bignum ( + ($rust_type: ty, $byte_size: expr, $is_little_endian: expr, $is_signed: expr) => ( + impl ToPyObject for $rust_type { + #[inline] + fn to_object(&self, py: Python) -> PyObject { + self.into_object(py) + } + } + impl IntoPyObject for $rust_type { + fn into_object(self, py: Python) -> PyObject { + unsafe { + let bytes = ::std::mem::transmute::<_, [c_uchar; $byte_size]>(self); + let obj = ffi::_PyLong_FromByteArray( + bytes.as_ptr() as *const c_uchar, + $byte_size, + $is_little_endian, + $is_signed, + ); + PyObject::from_owned_ptr_or_panic(py, obj) + } + } + } + impl<'source> FromPyObject<'source> for $rust_type { + fn extract(ob: &'source PyObjectRef) -> PyResult<$rust_type> { + unsafe { + let num = ffi::PyNumber_Index(ob.as_ptr()); + if num.is_null() { + return Err(PyErr::fetch(ob.py())); + } + let buffer: [c_uchar; $byte_size] = [0; $byte_size]; + let ok = ffi::_PyLong_AsByteArray( + ob.as_ptr() as *mut ffi::PyLongObject, + buffer.as_ptr() as *const c_uchar, + $byte_size, + $is_little_endian, + $is_signed, + ); + if ok == -1 { + Err(PyErr::fetch(ob.py())) + } else { + Ok(::std::mem::transmute::<_, $rust_type>(buffer)) + } + } + } + } + ) +); + +// manual implementation for 128bit integers +#[cfg(target_endian = "little")] +pub(super) const IS_LITTLE_ENDIAN: c_int = 1; +#[cfg(not(target_endian = "little"))] +pub(super) const IS_LITTLE_ENDIAN: c_int = 0; + +#[cfg(test)] +mod test { + use std; + use python::Python; + use conversion::ToPyObject; + + #[test] + fn test_u32_max() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::u32::MAX; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert_eq!(v as u64, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + fn test_i64_max() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::i64::MAX; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert_eq!(v as u64, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + fn test_i64_min() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::i64::MIN; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + fn test_u64_max() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::u64::MAX; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + #[cfg(not(Py_LIMITED_API))] + fn test_i128_max() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::i128::MAX; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert_eq!(v as u128, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + #[cfg(not(Py_LIMITED_API))] + fn test_i128_min() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::i128::MIN; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + #[cfg(not(Py_LIMITED_API))] + fn test_u128_max() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let v = std::u128::MAX; + let obj = v.to_object(py); + assert_eq!(v, obj.extract::(py).unwrap()); + assert!(obj.extract::(py).is_err()); + } + + #[test] + #[cfg(not(Py_LIMITED_API))] + fn test_u128_overflow() { + use ffi; + use object::PyObject; + use objects::exc; + use std::os::raw::c_uchar; + let gil = Python::acquire_gil(); + let py = gil.python(); + let overflow_bytes: [c_uchar; 20] = [255; 20]; + unsafe { + let obj = ffi::_PyLong_FromByteArray( + overflow_bytes.as_ptr() as *const c_uchar, + 20, + super::IS_LITTLE_ENDIAN, + 0, + ); + let obj = PyObject::from_owned_ptr_or_panic(py, obj); + let err = obj.extract::(py).unwrap_err(); + assert!(err.is_instance::(py)); + } + } +}