1#![cfg(feature = "num-bigint")]
2#![doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"), "\", features = [\"num-bigint\"] }")]
14#[cfg(Py_LIMITED_API)]
51use crate::types::{bytes::PyBytesMethods, PyBytes};
52use crate::{
53 conversion::IntoPyObject,
54 ffi,
55 instance::Bound,
56 types::{any::PyAnyMethods, PyInt},
57 FromPyObject, Py, PyAny, PyErr, PyResult, Python,
58};
59
60use num_bigint::{BigInt, BigUint};
61
62#[cfg(not(Py_LIMITED_API))]
63use num_bigint::Sign;
64
65macro_rules! bigint_conversion {
67 ($rust_ty: ty, $is_signed: literal, $to_bytes: path) => {
68 #[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
69 impl<'py> IntoPyObject<'py> for $rust_ty {
70 type Target = PyInt;
71 type Output = Bound<'py, Self::Target>;
72 type Error = PyErr;
73
74 #[inline]
75 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
76 (&self).into_pyobject(py)
77 }
78 }
79
80 #[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
81 impl<'py> IntoPyObject<'py> for &$rust_ty {
82 type Target = PyInt;
83 type Output = Bound<'py, Self::Target>;
84 type Error = PyErr;
85
86 #[cfg(not(Py_LIMITED_API))]
87 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
88 use crate::ffi_ptr_ext::FfiPtrExt;
89 let bytes = $to_bytes(&self);
90 unsafe {
91 Ok(ffi::_PyLong_FromByteArray(
92 bytes.as_ptr().cast(),
93 bytes.len(),
94 1,
95 $is_signed.into(),
96 )
97 .assume_owned(py)
98 .downcast_into_unchecked())
99 }
100 }
101
102 #[cfg(Py_LIMITED_API)]
103 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
104 use $crate::py_result_ext::PyResultExt;
105 let bytes = $to_bytes(&self);
106 let bytes_obj = PyBytes::new(py, &bytes);
107 let kwargs = if $is_signed {
108 let kwargs = crate::types::PyDict::new(py);
109 kwargs.set_item(crate::intern!(py, "signed"), true)?;
110 Some(kwargs)
111 } else {
112 None
113 };
114 unsafe {
115 py.get_type::<PyInt>()
116 .call_method("from_bytes", (bytes_obj, "little"), kwargs.as_ref())
117 .downcast_into_unchecked()
118 }
119 }
120 }
121 };
122}
123
124bigint_conversion!(BigUint, false, BigUint::to_bytes_le);
125bigint_conversion!(BigInt, true, BigInt::to_signed_bytes_le);
126
127#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
128impl<'py> FromPyObject<'py> for BigInt {
129 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<BigInt> {
130 let py = ob.py();
131 let num_owned: Py<PyInt>;
133 let num = if let Ok(long) = ob.downcast::<PyInt>() {
134 long
135 } else {
136 num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? };
137 num_owned.bind(py)
138 };
139 #[cfg(not(Py_LIMITED_API))]
140 {
141 let mut buffer = int_to_u32_vec::<true>(num)?;
142 let sign = if buffer.last().copied().is_some_and(|last| last >> 31 != 0) {
143 let mut elements = buffer.iter_mut();
146 for element in elements.by_ref() {
147 *element = (!*element).wrapping_add(1);
148 if *element != 0 {
149 break;
151 }
152 }
153 for element in elements {
155 *element = !*element;
156 }
157 Sign::Minus
158 } else {
159 Sign::Plus
160 };
161 Ok(BigInt::new(sign, buffer))
162 }
163 #[cfg(Py_LIMITED_API)]
164 {
165 let n_bits = int_n_bits(num)?;
166 if n_bits == 0 {
167 return Ok(BigInt::from(0isize));
168 }
169 let bytes = int_to_py_bytes(num, (n_bits + 8) / 8, true)?;
170 Ok(BigInt::from_signed_bytes_le(bytes.as_bytes()))
171 }
172 }
173}
174
175#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
176impl<'py> FromPyObject<'py> for BigUint {
177 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<BigUint> {
178 let py = ob.py();
179 let num_owned: Py<PyInt>;
181 let num = if let Ok(long) = ob.downcast::<PyInt>() {
182 long
183 } else {
184 num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? };
185 num_owned.bind(py)
186 };
187 #[cfg(not(Py_LIMITED_API))]
188 {
189 let buffer = int_to_u32_vec::<false>(num)?;
190 Ok(BigUint::new(buffer))
191 }
192 #[cfg(Py_LIMITED_API)]
193 {
194 let n_bits = int_n_bits(num)?;
195 if n_bits == 0 {
196 return Ok(BigUint::from(0usize));
197 }
198 let bytes = int_to_py_bytes(num, n_bits.div_ceil(8), false)?;
199 Ok(BigUint::from_bytes_le(bytes.as_bytes()))
200 }
201 }
202}
203
204#[cfg(not(any(Py_LIMITED_API, Py_3_13)))]
205#[inline]
206fn int_to_u32_vec<const SIGNED: bool>(long: &Bound<'_, PyInt>) -> PyResult<Vec<u32>> {
207 let mut buffer = Vec::new();
208 let n_bits = int_n_bits(long)?;
209 if n_bits == 0 {
210 return Ok(buffer);
211 }
212 let n_digits = if SIGNED {
213 (n_bits + 32) / 32
214 } else {
215 n_bits.div_ceil(32)
216 };
217 buffer.reserve_exact(n_digits);
218 unsafe {
219 crate::err::error_on_minusone(
220 long.py(),
221 ffi::_PyLong_AsByteArray(
222 long.as_ptr().cast(),
223 buffer.as_mut_ptr() as *mut u8,
224 n_digits * 4,
225 1,
226 SIGNED.into(),
227 ),
228 )?;
229 buffer.set_len(n_digits)
230 };
231 buffer
232 .iter_mut()
233 .for_each(|chunk| *chunk = u32::from_le(*chunk));
234
235 Ok(buffer)
236}
237
238#[cfg(all(not(Py_LIMITED_API), Py_3_13))]
239#[inline]
240fn int_to_u32_vec<const SIGNED: bool>(long: &Bound<'_, PyInt>) -> PyResult<Vec<u32>> {
241 let mut buffer = Vec::new();
242 let mut flags = ffi::Py_ASNATIVEBYTES_LITTLE_ENDIAN;
243 if !SIGNED {
244 flags |= ffi::Py_ASNATIVEBYTES_UNSIGNED_BUFFER | ffi::Py_ASNATIVEBYTES_REJECT_NEGATIVE;
245 }
246 let n_bytes =
247 unsafe { ffi::PyLong_AsNativeBytes(long.as_ptr().cast(), std::ptr::null_mut(), 0, flags) };
248 let n_bytes_unsigned: usize = n_bytes
249 .try_into()
250 .map_err(|_| crate::PyErr::fetch(long.py()))?;
251 if n_bytes == 0 {
252 return Ok(buffer);
253 }
254 let n_digits = n_bytes_unsigned.div_ceil(4);
255 buffer.reserve_exact(n_digits);
256 unsafe {
257 ffi::PyLong_AsNativeBytes(
258 long.as_ptr().cast(),
259 buffer.as_mut_ptr().cast(),
260 (n_digits * 4).try_into().unwrap(),
261 flags,
262 );
263 buffer.set_len(n_digits);
264 };
265 buffer
266 .iter_mut()
267 .for_each(|chunk| *chunk = u32::from_le(*chunk));
268
269 Ok(buffer)
270}
271
272#[cfg(Py_LIMITED_API)]
273fn int_to_py_bytes<'py>(
274 long: &Bound<'py, PyInt>,
275 n_bytes: usize,
276 is_signed: bool,
277) -> PyResult<Bound<'py, PyBytes>> {
278 use crate::intern;
279 let py = long.py();
280 let kwargs = if is_signed {
281 let kwargs = crate::types::PyDict::new(py);
282 kwargs.set_item(intern!(py, "signed"), true)?;
283 Some(kwargs)
284 } else {
285 None
286 };
287 let bytes = long.call_method(
288 intern!(py, "to_bytes"),
289 (n_bytes, intern!(py, "little")),
290 kwargs.as_ref(),
291 )?;
292 Ok(bytes.downcast_into()?)
293}
294
295#[inline]
296#[cfg(any(not(Py_3_13), Py_LIMITED_API))]
297fn int_n_bits(long: &Bound<'_, PyInt>) -> PyResult<usize> {
298 let py = long.py();
299 #[cfg(not(Py_LIMITED_API))]
300 {
301 let n_bits = unsafe { ffi::_PyLong_NumBits(long.as_ptr()) };
303 if n_bits == (-1isize as usize) {
304 return Err(crate::PyErr::fetch(py));
305 }
306 Ok(n_bits)
307 }
308
309 #[cfg(Py_LIMITED_API)]
310 {
311 long.call_method0(crate::intern!(py, "bit_length"))
313 .and_then(|any| any.extract())
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320 use crate::tests::common::generate_unique_module_name;
321 use crate::types::{PyDict, PyModule};
322 use indoc::indoc;
323 use pyo3_ffi::c_str;
324
325 fn rust_fib<T>() -> impl Iterator<Item = T>
326 where
327 T: From<u16>,
328 for<'a> &'a T: std::ops::Add<Output = T>,
329 {
330 let mut f0: T = T::from(1);
331 let mut f1: T = T::from(1);
332 std::iter::from_fn(move || {
333 let f2 = &f0 + &f1;
334 Some(std::mem::replace(&mut f0, std::mem::replace(&mut f1, f2)))
335 })
336 }
337
338 fn python_fib(py: Python<'_>) -> impl Iterator<Item = Bound<'_, PyAny>> + '_ {
339 let mut f0 = 1i32.into_pyobject(py).unwrap().into_any();
340 let mut f1 = 1i32.into_pyobject(py).unwrap().into_any();
341 std::iter::from_fn(move || {
342 let f2 = f0.call_method1("__add__", (&f1,)).unwrap();
343 Some(std::mem::replace(&mut f0, std::mem::replace(&mut f1, f2)))
344 })
345 }
346
347 #[test]
348 fn convert_biguint() {
349 Python::attach(|py| {
350 for (py_result, rs_result) in python_fib(py).zip(rust_fib::<BigUint>()).take(2000) {
352 assert_eq!(py_result.extract::<BigUint>().unwrap(), rs_result);
354 assert!(py_result.eq(rs_result).unwrap());
356 }
357 });
358 }
359
360 #[test]
361 fn convert_bigint() {
362 Python::attach(|py| {
363 for (py_result, rs_result) in python_fib(py).zip(rust_fib::<BigInt>()).take(2000) {
365 assert_eq!(py_result.extract::<BigInt>().unwrap(), rs_result);
367 assert!(py_result.eq(&rs_result).unwrap());
369
370 let rs_result = rs_result * -1;
373 let py_result = py_result.call_method0("__neg__").unwrap();
374
375 assert_eq!(py_result.extract::<BigInt>().unwrap(), rs_result);
377 assert!(py_result.eq(rs_result).unwrap());
379 }
380 });
381 }
382
383 fn python_index_class(py: Python<'_>) -> Bound<'_, PyModule> {
384 let index_code = c_str!(indoc!(
385 r#"
386 class C:
387 def __init__(self, x):
388 self.x = x
389 def __index__(self):
390 return self.x
391 "#
392 ));
393 PyModule::from_code(
394 py,
395 index_code,
396 c_str!("index.py"),
397 &generate_unique_module_name("index"),
398 )
399 .unwrap()
400 }
401
402 #[test]
403 fn convert_index_class() {
404 Python::attach(|py| {
405 let index = python_index_class(py);
406 let locals = PyDict::new(py);
407 locals.set_item("index", index).unwrap();
408 let ob = py
409 .eval(ffi::c_str!("index.C(10)"), None, Some(&locals))
410 .unwrap();
411 let _: BigInt = ob.extract().unwrap();
412 });
413 }
414
415 #[test]
416 fn handle_zero() {
417 Python::attach(|py| {
418 let zero: BigInt = 0i32.into_pyobject(py).unwrap().extract().unwrap();
419 assert_eq!(zero, BigInt::from(0));
420 })
421 }
422
423 #[test]
425 fn check_overflow() {
426 Python::attach(|py| {
427 macro_rules! test {
428 ($T:ty, $value:expr, $py:expr) => {
429 let value = $value;
430 println!("{}: {}", stringify!($T), value);
431 let python_value = value.clone().into_pyobject(py).unwrap();
432 let roundtrip_value = python_value.extract::<$T>().unwrap();
433 assert_eq!(value, roundtrip_value);
434 };
435 }
436
437 for i in 0..=256usize {
438 test!(BigInt, BigInt::from(i), py);
440 test!(BigUint, BigUint::from(i), py);
441 test!(BigInt, -BigInt::from(i), py);
442 test!(BigInt, BigInt::from(1) << i, py);
443 test!(BigUint, BigUint::from(1u32) << i, py);
444 test!(BigInt, -BigInt::from(1) << i, py);
445 test!(BigInt, (BigInt::from(1) << i) + 1u32, py);
446 test!(BigUint, (BigUint::from(1u32) << i) + 1u32, py);
447 test!(BigInt, (-BigInt::from(1) << i) + 1u32, py);
448 test!(BigInt, (BigInt::from(1) << i) - 1u32, py);
449 test!(BigUint, (BigUint::from(1u32) << i) - 1u32, py);
450 test!(BigInt, (-BigInt::from(1) << i) - 1u32, py);
451 }
452 });
453 }
454}