Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement 128bit integer conversion for limited API #1310

Merged
merged 2 commits into from Dec 12, 2020
Merged

Conversation

kngwyu
Copy link
Member

@kngwyu kngwyu commented Dec 12, 2020

Fix #1309
They would be slow since they use eval... but could there be any alternative?

@birkenfeld
Copy link
Member

Why use eval when you can call the method directly with call_method?

@birkenfeld
Copy link
Member

birkenfeld commented Dec 12, 2020

This is the snippet I tested:

        let bytes = obj.call_method("to_bytes", (16, "little"), None)?;
        let res = u128::from_le_bytes(bytes.extract()?);

Or do you think this is slower due to conversion of the arguments from Rust? Hm, you need to construct a whole dictionary for the eval...

Of course, both are missing the PyNumber_Index call that the other conversion is doing.

@birkenfeld
Copy link
Member

Alternative, as I said in #1309, is to use PyNumber_ APIs to shift and mask the number to get two values that fit into u64's.

@kngwyu
Copy link
Member Author

kngwyu commented Dec 12, 2020

Why use eval when you can call the method directly with call_method?

Thanks, I just failed to come up with it. We can call PyNumber_Index manually.

to use PyNumber_ APIs to shift and mask the number to get two values that fit into u64's.

I tried that first but I found it too complex, especially for i128, though it is faster...

@birkenfeld
Copy link
Member

So in my tests (FromPyObject only) it was slightly faster to call with call_method, but then use your method of downcast instead of extract.

@@ -183,11 +173,12 @@ mod int128_conversion {
impl IntoPy<PyObject> for $rust_type {
fn into_py(self, py: Python) -> PyObject {
unsafe {
let bytes = self.to_ne_bytes();
// Always use little endian
let bytes = self.to_le_bytes();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though it's not related to this PR, I noticed that to_le_bytes is added in Rust 1.32.
Using always little-endian simplified the code a lot.

let obj = ffi::_PyLong_FromByteArray(
bytes.as_ptr() as *const c_uchar,
$byte_size,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why 16 is not hard-coded...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe bytes.len() is most correct? I would think that the compiler will still optimize this at compile time.

Copy link
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

I think it's better to use std::mem::size_of{_val} instead of hard-coding 16, as it's easier to understand what that means for a future reader rather than just seeing a magic number 16.

src/types/num.rs Show resolved Hide resolved
let obj = ffi::_PyLong_FromByteArray(
bytes.as_ptr() as *const c_uchar,
$byte_size,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe bytes.len() is most correct? I would think that the compiler will still optimize this at compile time.

src/types/num.rs Outdated
@@ -202,83 +193,131 @@ mod int128_conversion {
if num.is_null() {
return Err(PyErr::fetch(ob.py()));
}
let mut buffer = [0; $byte_size];
let mut buffer = [0; 16];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let mut buffer = [0; 16];
let mut buffer = [0; std::mem::size_of::<$rust_type>()];

src/types/num.rs Outdated
let ok = ffi::_PyLong_AsByteArray(
num as *mut ffi::PyLongObject,
buffer.as_mut_ptr(),
$byte_size,
IS_LITTLE_ENDIAN,
16,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
16,
buffer.len(),

src/types/num.rs Outdated
let num = unsafe { ffi::PyNumber_Index(ob.as_ptr()) };
let num: &PyLong =
unsafe { crate::FromPyPointer::from_owned_ptr_or_err(py, num)? };
let args = (16, "little".to_object(py));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let args = (16, "little".to_object(py));
let args = (std::mem::size_of::<$rust_type>(), "little".to_object(py));

or could define a const SIZE: usize = std::mem::size_of::<$rust_type>() and use here and on line 258.

src/types/num.rs Outdated
ffi::PyNumber_Index(ob.as_ptr()),
)
}?;
let kwargs = [("signed", $is_signed)].into_py_dict(py);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah damn, it requires a dict here too then. Can you at least make this

let kwargs = if $is_signed { Some(...) } else { None };

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure,

CHANGELOG.md Outdated Show resolved Hide resolved
Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com>
@kngwyu
Copy link
Member Author

kngwyu commented Dec 12, 2020

Thank you for your reviews!

Copy link
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

abi3 causes compile failures where u128s are involved
3 participants