Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/capi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
bitflags = { workspace = true }
itertools = { workspace = true }
malachite-bigint = { workspace = true }
num-complex = { workspace = true }
rustpython-vm = { workspace = true, features = ["threading", "compiler", "importlib", "host_env"] }
rustpython-stdlib = {workspace = true, features = ["threading"] }
Expand Down
169 changes: 168 additions & 1 deletion crates/capi/src/longobject.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::PyObject;
use crate::object::define_py_check;
use crate::pystate::with_vm;
use core::ffi::{c_long, c_longlong, c_ulong, c_ulonglong};
use core::ffi::{c_int, c_long, c_longlong, c_ulong, c_ulonglong, c_void};
use malachite_bigint::{BigInt, BigUint, Sign};
use rustpython_vm::PyResult;
use rustpython_vm::builtins::PyInt;

Expand Down Expand Up @@ -64,6 +65,172 @@ pub unsafe extern "C" fn PyLong_AsUnsignedLongLong(obj: *mut PyObject) -> c_ulon
})
}

#[repr(C)]
pub struct PyLongLayout {
pub bits_per_digit: u8,
pub digit_size: u8,
pub digits_order: i8,
pub digit_endianness: i8,
}

#[repr(C)]
#[derive(Default)]
pub struct PyLongExport {
pub value: i64,
pub negative: u8,
pub ndigits: isize,
pub digits: *const c_void,
_reserved: *mut Vec<u32>,
}

pub struct PyLongWriter {
negative: bool,
digits: Vec<u32>,
}

#[unsafe(no_mangle)]
pub extern "C" fn PyLong_GetNativeLayout() -> *const PyLongLayout {
const NATIVE_LONG_LAYOUT: PyLongLayout = PyLongLayout {
bits_per_digit: 32,
digit_size: 4,
digits_order: -1,
digit_endianness: if cfg!(target_endian = "little") {
-1
} else {
1
},
};
&NATIVE_LONG_LAYOUT
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_Export(
obj: *mut PyObject,
export_long: *mut PyLongExport,
) -> c_int {
with_vm::<PyResult<()>, _>(|vm| {
let py_int = unsafe { &*obj }.try_downcast_ref::<PyInt>(vm)?;
let bigint = py_int.as_bigint();

if let Ok(value) = i64::try_from(bigint) {
unsafe {
*export_long = PyLongExport {
value,
..Default::default()
};
}
return Ok(());
}

let (sign, digits) = bigint.to_u32_digits();
let boxed_digits = Box::new(digits);
let ndigits = boxed_digits.len().try_into().map_err(|_| {
vm.new_overflow_error("PyLong_Export: too many digits to fit into Py_ssize_t")
})?;

unsafe {
*export_long = PyLongExport {
value: 0,
negative: u8::from(matches!(sign, Sign::Minus)),
ndigits,
digits: boxed_digits.as_ptr().cast(),
_reserved: Box::into_raw(boxed_digits),
};
}
Ok(())
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_FreeExport(export_long: *mut PyLongExport) {
if export_long.is_null() {
return;
}

let export_long = unsafe { &mut *export_long };
if !export_long._reserved.is_null() {
unsafe {
drop(Box::from_raw(export_long._reserved));
}
}
core::mem::take(export_long);
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLongWriter_Create(
negative: c_int,
ndigits: isize,
digits: *mut *mut c_void,
) -> *mut PyLongWriter {
with_vm::<PyResult<*mut c_void>, _>(|vm| {
if ndigits <= 0 {
return Err(vm.new_value_error("PyLongWriter_Create: ndigits must be greater than 0"));
}
if digits.is_null() {
return Err(vm.new_system_error("PyLongWriter_Create: digits must not be null"));
}
if negative != 0 && negative != 1 {
return Err(vm.new_value_error("PyLongWriter_Create: negative must be 0 or 1"));
}

let ndigits = ndigits
.try_into()
.map_err(|_| vm.new_overflow_error("PyLongWriter_Create: ndigits out of range"))?;

let mut writer = Box::new(PyLongWriter {
negative: negative == 1,
digits: vec![0; ndigits],
});

unsafe {
*digits = writer.digits.as_mut_ptr().cast();
}

Ok(Box::into_raw(writer).cast())
})
.cast()
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLongWriter_Finish(writer: *mut PyLongWriter) -> *mut PyObject {
with_vm(|vm| {
if writer.is_null() {
return Err(vm.new_system_error("PyLongWriter_Finish: writer must not be null"));
}

let writer = unsafe { Box::from_raw(writer) };
let mut digits = writer.digits;
while matches!(digits.last(), Some(0)) {
digits.pop();
}

if digits.is_empty() {
return Ok(vm.ctx.new_int(0));
}

let magnitude = BigUint::new(digits);
let sign = if writer.negative {
Sign::Minus
} else {
Sign::Plus
};

let value = BigInt::from_biguint(sign, magnitude);
Ok(vm.ctx.new_int(value))
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLongWriter_Discard(writer: *mut PyLongWriter) {
if writer.is_null() {
return;
}

unsafe {
drop(Box::from_raw(writer));
}
}

#[cfg(false)]
mod tests {
use pyo3::prelude::*;
Expand Down
Loading