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