Skip to content
Open
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 crates/capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use rustpython_vm::{Context, Interpreter};
use std::sync::MutexGuard;

extern crate alloc;
extern crate core;

pub mod abstract_;
pub mod boolobject;
Expand Down
200 changes: 198 additions & 2 deletions crates/capi/src/longobject.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
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_double, c_int, c_long, c_longlong, c_ulong, c_ulonglong, c_void};
use rustpython_vm::PyResult;
use rustpython_vm::builtins::PyInt;
use rustpython_vm::builtins::{PyInt, try_f64_to_bigint};

define_py_check!(fn PyLong_Check, types.int_type);
define_py_check!(exact fn PyLong_CheckExact, types.int_type);
Expand Down Expand Up @@ -38,6 +38,36 @@ pub extern "C" fn PyLong_FromUnsignedLongLong(value: c_ulonglong) -> *mut PyObje
with_vm(|vm| vm.ctx.new_int(value))
}

#[unsafe(no_mangle)]
pub extern "C" fn PyLong_FromDouble(value: c_double) -> *mut PyObject {
with_vm(|vm| Ok(vm.ctx.new_bigint(&try_f64_to_bigint(value, vm)?)))
}

#[unsafe(no_mangle)]
pub extern "C" fn PyLong_FromInt32(value: i32) -> *mut PyObject {
with_vm(|vm| vm.ctx.new_int(value))
}

#[unsafe(no_mangle)]
pub extern "C" fn PyLong_FromInt64(value: i64) -> *mut PyObject {
with_vm(|vm| vm.ctx.new_int(value))
}

#[unsafe(no_mangle)]
pub extern "C" fn PyLong_FromUInt32(value: u32) -> *mut PyObject {
with_vm(|vm| vm.ctx.new_int(value))
}

#[unsafe(no_mangle)]
pub extern "C" fn PyLong_FromUInt64(value: u64) -> *mut PyObject {
with_vm(|vm| vm.ctx.new_int(value))
}

#[unsafe(no_mangle)]
pub extern "C" fn PyLong_FromVoidPtr(ptr: *mut c_void) -> *mut PyObject {
with_vm(|vm| vm.ctx.new_int(ptr as usize))
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsLong(obj: *mut PyObject) -> c_long {
with_vm::<PyResult<c_long>, _>(|vm| {
Expand All @@ -50,6 +80,163 @@ pub unsafe extern "C" fn PyLong_AsLong(obj: *mut PyObject) -> c_long {
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsInt(obj: *mut PyObject) -> c_int {
with_vm::<PyResult<c_int>, _>(|vm| {
unsafe { &*obj }
.to_owned()
.try_index(vm)?
.as_bigint()
.try_into()
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C int"))
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsInt32(obj: *mut PyObject, out: *mut i32) -> c_int {
with_vm(|vm| {
let value: i32 = unsafe { &*obj }
.to_owned()
.try_index(vm)?
.as_bigint()
.try_into()
.map_err(|_| vm.new_overflow_error("Python int too large to convert to int32_t"))?;
unsafe { *out = value };
Ok(())
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsInt64(obj: *mut PyObject, out: *mut i64) -> c_int {
with_vm(|vm| {
let value: i64 = unsafe { &*obj }
.to_owned()
.try_index(vm)?
.as_bigint()
.try_into()
.map_err(|_| vm.new_overflow_error("Python int too large to convert to int64_t"))?;
unsafe { *out = value };
Ok(())
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsLongLong(obj: *mut PyObject) -> c_longlong {
with_vm::<PyResult<c_longlong>, _>(|vm| {
unsafe { &*obj }
.to_owned()
.try_index(vm)?
.as_bigint()
.try_into()
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C long long"))
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsSize_t(obj: *mut PyObject) -> usize {
with_vm::<PyResult<usize>, _>(|vm| {
let value: usize = unsafe { &*obj }
.to_owned()
.try_index(vm)?
.as_bigint()
.try_into()
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C size_t"))?;
Ok(value)
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsSsize_t(obj: *mut PyObject) -> isize {
with_vm::<PyResult<isize>, _>(|vm| {
unsafe { &*obj }
.to_owned()
.try_index(vm)?
.as_bigint()
.try_into()
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C ssize_t"))
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsUInt32(obj: *mut PyObject, out: *mut u32) -> c_int {
with_vm(|vm| {
let value: u32 = unsafe { &*obj }
.to_owned()
.try_index(vm)?
.as_bigint()
.try_into()
.map_err(|_| vm.new_overflow_error("Python int too large to convert to uint32_t"))?;
unsafe { *out = value };
Ok(())
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsUInt64(obj: *mut PyObject, out: *mut u64) -> c_int {
with_vm(|vm| {
let value: u64 = unsafe { &*obj }
.to_owned()
.try_index(vm)?
.as_bigint()
.try_into()
.map_err(|_| vm.new_overflow_error("Python int too large to convert to uint64_t"))?;
unsafe { *out = value };
Ok(())
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsUnsignedLong(obj: *mut PyObject) -> c_ulong {
with_vm::<PyResult<c_ulong>, _>(|vm| {
unsafe { &*obj }
.to_owned()
.try_index(vm)?
.as_bigint()
.try_into()
.map_err(|_| {
vm.new_overflow_error("Python int too large to convert to C unsigned long")
})
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsUnsignedLongMask(obj: *mut PyObject) -> c_ulong {
with_vm::<PyResult<c_ulong>, _>(|vm| {
let int = unsafe { &*obj }.to_owned().try_index(vm)?;
if const { c_ulong::BITS == 32 } {
Ok(c_ulong::from(int.as_u32_mask()))
} else {
Ok(int.as_u64_mask() as c_ulong)
}
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsUnsignedLongLongMask(obj: *mut PyObject) -> c_ulonglong {
with_vm::<PyResult<c_ulonglong>, _>(|vm| {
let int = unsafe { &*obj }.to_owned().try_index(vm)?;
Ok(int.as_u64_mask())
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsVoidPtr(obj: *mut PyObject) -> *mut c_void {
with_vm(|vm| {
let value = unsafe { &*obj }.to_owned().try_index(vm)?;

let unsigned: Result<usize, _> = value.as_bigint().try_into();
if let Ok(v) = unsigned {
return Ok(v as *mut c_void);
}
let signed: Result<isize, _> = value.as_bigint().try_into();
if let Ok(v) = signed {
return Ok((v as usize) as *mut c_void);
}

Err(vm.new_overflow_error("int too large to convert to pointer"))
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyLong_AsUnsignedLongLong(obj: *mut PyObject) -> c_ulonglong {
with_vm::<PyResult<c_ulonglong>, _>(|vm| {
Expand Down Expand Up @@ -86,4 +273,13 @@ mod tests {
assert_eq!(number.extract::<u64>().unwrap(), 123);
})
}

#[test]
fn py_int_u128() {
Python::attach(|py| {
let value = 1u128 << 100;
let number = PyInt::new(py, value);
assert_eq!(number.extract::<u128>().unwrap(), value);
})
}
}
83 changes: 81 additions & 2 deletions crates/capi/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::PyObject;
use core::convert::Infallible;
use core::ffi::{c_char, c_double, c_int, c_long, c_ulonglong, c_void};
use core::ffi::{c_char, c_double, c_int, c_long, c_ulong, c_void};
use rustpython_vm::{PyObjectRef, PyRef, PyResult, VirtualMachine};

pub(crate) trait FfiResult<Output = Self> {
Expand Down Expand Up @@ -101,6 +101,31 @@ impl FfiResult<isize> for usize {
}
}

#[cfg(not(windows))]
impl FfiResult for c_int {
const ERR_VALUE: Self = -1;

fn into_output(self, _vm: &VirtualMachine) -> Self {
self
}
}

impl FfiResult for usize {
const ERR_VALUE: Self = Self::MAX;

fn into_output(self, _vm: &VirtualMachine) -> Self {
self
}
}

impl FfiResult for isize {
const ERR_VALUE: Self = -1;

fn into_output(self, _vm: &VirtualMachine) -> Self {
self
}
}

impl FfiResult for c_long {
const ERR_VALUE: Self = -1;

Expand All @@ -109,7 +134,25 @@ impl FfiResult for c_long {
}
}

impl FfiResult for c_ulonglong {
impl FfiResult for c_ulong {
const ERR_VALUE: Self = Self::MAX;

fn into_output(self, _vm: &VirtualMachine) -> Self {
self
}
}

#[cfg(windows)]
impl FfiResult for core::ffi::c_longlong {
const ERR_VALUE: Self = -1;

fn into_output(self, _vm: &VirtualMachine) -> Self {
self
}
}

#[cfg(windows)]
impl FfiResult for core::ffi::c_ulonglong {
const ERR_VALUE: Self = Self::MAX;

fn into_output(self, _vm: &VirtualMachine) -> Self {
Expand Down Expand Up @@ -159,3 +202,39 @@ where
)
}
}

#[cfg(test)]
mod tests {
use super::*;
use core::fmt::Debug;
use std::any::type_name;
use std::ffi::{c_longlong, c_ulonglong};

#[test]
fn ffi_result_err_value() {
fn assert_error_value<T, Output>(value: Output)
where
T: FfiResult<Output> + 'static,
Output: PartialEq + Debug,
{
assert_eq!(value, T::ERR_VALUE, "{}", type_name::<T>(),);
}

assert_error_value::<(), _>(());
assert_error_value::<(), c_int>(-1);

assert_error_value::<isize, _>(-1);
assert_error_value::<usize, _>(usize::MAX);
assert_error_value::<usize, isize>(-1);
assert_error_value::<c_int, _>(-1); // i32
assert_error_value::<c_long, _>(-1); //Windows i32, unix i64
assert_error_value::<c_ulong, _>(c_ulong::MAX); // Windows u32, unix u64
assert_error_value::<c_longlong, _>(-1); // i64
assert_error_value::<c_ulonglong, _>(c_ulonglong::MAX); // u64
assert_error_value::<c_double, _>(-1.0);
assert_error_value::<bool, _>(-1);

assert_error_value::<PyResult<c_int>, _>(-1);
assert_error_value::<PyResult<usize>, _>(usize::MAX);
}
}
22 changes: 18 additions & 4 deletions crates/vm/src/builtins/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,12 +322,26 @@ impl PyInt {
v.to_u32()
.or_else(|| v.to_i32().map(|i| i as u32))
.unwrap_or_else(|| {
let mut out = 0u32;
for digit in v.iter_u32_digits() {
out = out.wrapping_shl(32) | digit;
let out = v.iter_u32_digits().next().unwrap_or(0);
match v.sign() {
Sign::Minus => out.wrapping_neg(),
_ => out,
}
})
}

// _PyLong_AsUnsignedLongLongMask
#[must_use]
pub fn as_u64_mask(&self) -> u64 {
let v = self.as_bigint();
v.to_u64()
.or_else(|| v.to_i64().map(|i| i as u64))
.unwrap_or_else(|| {
let mut digits = v.iter_u32_digits();
let out = u64::from(digits.next().unwrap_or(0))
| (u64::from(digits.next().unwrap_or(0)) << 32);
match v.sign() {
Sign::Minus => out * -1i32 as u32,
Sign::Minus => out.wrapping_neg(),
_ => out,
}
})
Expand Down
Loading