Skip to content

Commit

Permalink
Add abi3 + num_bigint conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowone committed Jun 2, 2023
1 parent 451729a commit 0cedf87
Showing 1 changed file with 68 additions and 4 deletions.
72 changes: 68 additions & 4 deletions src/conversions/num_bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// based on Daniel Grunwald's /~https://github.com/dgrunwald/rust-cpython

#![cfg(all(feature = "num-bigint", not(any(Py_LIMITED_API))))]
#![cfg(all(feature = "num-bigint"))]
//! Conversions to and from [num-bigint](https://docs.rs/num-bigint)’s [`BigInt`] and [`BigUint`] types.
//!
//! This is useful for converting Python integers when they may not fit in Rust's built-in integer types.
Expand Down Expand Up @@ -57,13 +57,14 @@
//! ```
use crate::{
err, ffi, types::*, AsPyPointer, FromPyObject, IntoPy, Py, PyAny, PyErr, PyObject, PyResult,
Python, ToPyObject,
err, ffi, intern, types::*, AsPyPointer, FromPyObject, IntoPy, Py, PyAny, PyErr, PyObject,
PyResult, Python, ToPyObject,
};

use num_bigint::{BigInt, BigUint};
use std::os::raw::{c_int, c_uchar};

#[cfg(not(Py_LIMITED_API))]
unsafe fn extract(ob: &PyLong, buffer: &mut [c_uchar], is_signed: c_int) -> PyResult<()> {
err::error_on_minusone(
ob.py(),
Expand All @@ -77,13 +78,32 @@ unsafe fn extract(ob: &PyLong, buffer: &mut [c_uchar], is_signed: c_int) -> PyRe
)
}

#[cfg(Py_LIMITED_API)]
unsafe fn extract(ob: &PyLong, buffer: &mut [c_uchar], is_signed: bool) -> PyResult<()> {
let py = ob.py();
let kwargs = if is_signed {
let kwargs = PyDict::new(py);
kwargs.set_item(intern!(py, "signed"), true)?;
Some(kwargs)
} else {
None
};
let bytes_obj = ob
.getattr(intern!(py, "to_bytes"))?
.call((buffer.len(), "little"), kwargs)?;
let bytes: &PyBytes = bytes_obj.downcast_unchecked();
buffer.copy_from_slice(bytes.as_bytes());
Ok(())
}

macro_rules! bigint_conversion {
($rust_ty: ty, $is_signed: expr, $to_bytes: path, $from_bytes: path) => {
#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
impl ToPyObject for $rust_ty {
#[cfg(not(Py_LIMITED_API))]
fn to_object(&self, py: Python<'_>) -> PyObject {
let bytes = $to_bytes(self);
unsafe {
let bytes = $to_bytes(self);
let obj = ffi::_PyLong_FromByteArray(
bytes.as_ptr() as *const c_uchar,
bytes.len(),
Expand All @@ -93,6 +113,23 @@ macro_rules! bigint_conversion {
PyObject::from_owned_ptr(py, obj)
}
}

#[cfg(Py_LIMITED_API)]
fn to_object(&self, py: Python<'_>) -> PyObject {
let bytes = $to_bytes(self);
let bytes_obj = PyBytes::new(py, &bytes);
let kwargs = if $is_signed > 0 {
let kwargs = PyDict::new(py);
kwargs.set_item(intern!(py, "signed"), true).unwrap();
Some(kwargs)
} else {
None
};
py.get_type::<PyLong>()
.call_method("from_bytes", (bytes_obj, "little"), kwargs)
.expect("TODO")
.into()
}
}

#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
Expand All @@ -104,6 +141,7 @@ macro_rules! bigint_conversion {

#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
impl<'source> FromPyObject<'source> for $rust_ty {
#[cfg(not(Py_LIMITED_API))]
fn extract(ob: &'source PyAny) -> PyResult<$rust_ty> {
let py = ob.py();
unsafe {
Expand All @@ -128,6 +166,32 @@ macro_rules! bigint_conversion {
}
}
}

#[cfg(Py_LIMITED_API)]
fn extract(ob: &'source PyAny) -> PyResult<$rust_ty> {
let py = ob.py();
unsafe {
let num: Py<PyLong> =
Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))?;
let n_bits_obj = num.getattr(py, intern!(py, "bit_length"))?.call0(py)?;
let n_bits_int: &PyLong = n_bits_obj.downcast_unchecked(py);
let n_bits = n_bits_int.extract::<usize>()?;
let n_bytes = if n_bits == 0 {
0
} else {
(n_bits - 1 + $is_signed) / 8 + 1
};
if n_bytes <= 128 {
let mut buffer = [0; 128];
extract(num.as_ref(py), &mut buffer[..n_bytes], $is_signed > 0)?;
Ok($from_bytes(&buffer[..n_bytes]))
} else {
let mut buffer = vec![0; n_bytes];
extract(num.as_ref(py), &mut buffer, $is_signed > 0)?;
Ok($from_bytes(&buffer))
}
}
}
}
};
}
Expand Down

0 comments on commit 0cedf87

Please sign in to comment.