Skip to content

Commit 5c916a4

Browse files
Add more int functions to the c-api
1 parent 8905b80 commit 5c916a4

4 files changed

Lines changed: 298 additions & 8 deletions

File tree

crates/capi/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustpython_vm::{Context, Interpreter};
77
use std::sync::MutexGuard;
88

99
extern crate alloc;
10+
extern crate core;
1011

1112
pub mod abstract_;
1213
pub mod boolobject;

crates/capi/src/longobject.rs

Lines changed: 198 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use crate::PyObject;
22
use crate::object::define_py_check;
33
use crate::pystate::with_vm;
4-
use core::ffi::{c_long, c_longlong, c_ulong, c_ulonglong};
4+
use core::ffi::{c_double, c_int, c_long, c_longlong, c_ulong, c_ulonglong, c_void};
55
use rustpython_vm::PyResult;
6-
use rustpython_vm::builtins::PyInt;
6+
use rustpython_vm::builtins::{PyInt, try_f64_to_bigint};
77

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

41+
#[unsafe(no_mangle)]
42+
pub extern "C" fn PyLong_FromDouble(value: c_double) -> *mut PyObject {
43+
with_vm(|vm| Ok(vm.ctx.new_bigint(&try_f64_to_bigint(value, vm)?)))
44+
}
45+
46+
#[unsafe(no_mangle)]
47+
pub extern "C" fn PyLong_FromInt32(value: i32) -> *mut PyObject {
48+
with_vm(|vm| vm.ctx.new_int(value))
49+
}
50+
51+
#[unsafe(no_mangle)]
52+
pub extern "C" fn PyLong_FromInt64(value: i64) -> *mut PyObject {
53+
with_vm(|vm| vm.ctx.new_int(value))
54+
}
55+
56+
#[unsafe(no_mangle)]
57+
pub extern "C" fn PyLong_FromUInt32(value: u32) -> *mut PyObject {
58+
with_vm(|vm| vm.ctx.new_int(value))
59+
}
60+
61+
#[unsafe(no_mangle)]
62+
pub extern "C" fn PyLong_FromUInt64(value: u64) -> *mut PyObject {
63+
with_vm(|vm| vm.ctx.new_int(value))
64+
}
65+
66+
#[unsafe(no_mangle)]
67+
pub extern "C" fn PyLong_FromVoidPtr(ptr: *mut c_void) -> *mut PyObject {
68+
with_vm(|vm| vm.ctx.new_int(ptr as usize))
69+
}
70+
4171
#[unsafe(no_mangle)]
4272
pub unsafe extern "C" fn PyLong_AsLong(obj: *mut PyObject) -> c_long {
4373
with_vm::<PyResult<c_long>, _>(|vm| {
@@ -50,6 +80,163 @@ pub unsafe extern "C" fn PyLong_AsLong(obj: *mut PyObject) -> c_long {
5080
})
5181
}
5282

83+
#[unsafe(no_mangle)]
84+
pub unsafe extern "C" fn PyLong_AsInt(obj: *mut PyObject) -> c_int {
85+
with_vm::<PyResult<c_int>, _>(|vm| {
86+
unsafe { &*obj }
87+
.to_owned()
88+
.try_index(vm)?
89+
.as_bigint()
90+
.try_into()
91+
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C int"))
92+
})
93+
}
94+
95+
#[unsafe(no_mangle)]
96+
pub unsafe extern "C" fn PyLong_AsInt32(obj: *mut PyObject, out: *mut i32) -> c_int {
97+
with_vm(|vm| {
98+
let value: i32 = unsafe { &*obj }
99+
.to_owned()
100+
.try_index(vm)?
101+
.as_bigint()
102+
.try_into()
103+
.map_err(|_| vm.new_overflow_error("Python int too large to convert to int32_t"))?;
104+
unsafe { *out = value };
105+
Ok(())
106+
})
107+
}
108+
109+
#[unsafe(no_mangle)]
110+
pub unsafe extern "C" fn PyLong_AsInt64(obj: *mut PyObject, out: *mut i64) -> c_int {
111+
with_vm(|vm| {
112+
let value: i64 = unsafe { &*obj }
113+
.to_owned()
114+
.try_index(vm)?
115+
.as_bigint()
116+
.try_into()
117+
.map_err(|_| vm.new_overflow_error("Python int too large to convert to int64_t"))?;
118+
unsafe { *out = value };
119+
Ok(())
120+
})
121+
}
122+
123+
#[unsafe(no_mangle)]
124+
pub unsafe extern "C" fn PyLong_AsLongLong(obj: *mut PyObject) -> c_longlong {
125+
with_vm::<PyResult<c_longlong>, _>(|vm| {
126+
unsafe { &*obj }
127+
.to_owned()
128+
.try_index(vm)?
129+
.as_bigint()
130+
.try_into()
131+
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C long long"))
132+
})
133+
}
134+
135+
#[unsafe(no_mangle)]
136+
pub unsafe extern "C" fn PyLong_AsSize_t(obj: *mut PyObject) -> usize {
137+
with_vm::<PyResult<usize>, _>(|vm| {
138+
let value: usize = unsafe { &*obj }
139+
.to_owned()
140+
.try_index(vm)?
141+
.as_bigint()
142+
.try_into()
143+
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C size_t"))?;
144+
Ok(value)
145+
})
146+
}
147+
148+
#[unsafe(no_mangle)]
149+
pub unsafe extern "C" fn PyLong_AsSsize_t(obj: *mut PyObject) -> isize {
150+
with_vm::<PyResult<isize>, _>(|vm| {
151+
unsafe { &*obj }
152+
.to_owned()
153+
.try_index(vm)?
154+
.as_bigint()
155+
.try_into()
156+
.map_err(|_| vm.new_overflow_error("Python int too large to convert to C ssize_t"))
157+
})
158+
}
159+
160+
#[unsafe(no_mangle)]
161+
pub unsafe extern "C" fn PyLong_AsUInt32(obj: *mut PyObject, out: *mut u32) -> c_int {
162+
with_vm(|vm| {
163+
let value: u32 = unsafe { &*obj }
164+
.to_owned()
165+
.try_index(vm)?
166+
.as_bigint()
167+
.try_into()
168+
.map_err(|_| vm.new_overflow_error("Python int too large to convert to uint32_t"))?;
169+
unsafe { *out = value };
170+
Ok(())
171+
})
172+
}
173+
174+
#[unsafe(no_mangle)]
175+
pub unsafe extern "C" fn PyLong_AsUInt64(obj: *mut PyObject, out: *mut u64) -> c_int {
176+
with_vm(|vm| {
177+
let value: u64 = unsafe { &*obj }
178+
.to_owned()
179+
.try_index(vm)?
180+
.as_bigint()
181+
.try_into()
182+
.map_err(|_| vm.new_overflow_error("Python int too large to convert to uint64_t"))?;
183+
unsafe { *out = value };
184+
Ok(())
185+
})
186+
}
187+
188+
#[unsafe(no_mangle)]
189+
pub unsafe extern "C" fn PyLong_AsUnsignedLong(obj: *mut PyObject) -> c_ulong {
190+
with_vm::<PyResult<c_ulong>, _>(|vm| {
191+
unsafe { &*obj }
192+
.to_owned()
193+
.try_index(vm)?
194+
.as_bigint()
195+
.try_into()
196+
.map_err(|_| {
197+
vm.new_overflow_error("Python int too large to convert to C unsigned long")
198+
})
199+
})
200+
}
201+
202+
#[unsafe(no_mangle)]
203+
pub unsafe extern "C" fn PyLong_AsUnsignedLongMask(obj: *mut PyObject) -> c_ulong {
204+
with_vm::<PyResult<c_ulong>, _>(|vm| {
205+
let int = unsafe { &*obj }.to_owned().try_index(vm)?;
206+
if const { c_ulong::BITS == 32 } {
207+
Ok(c_ulong::from(int.as_u32_mask()))
208+
} else {
209+
Ok(int.as_u64_mask() as c_ulong)
210+
}
211+
})
212+
}
213+
214+
#[unsafe(no_mangle)]
215+
pub unsafe extern "C" fn PyLong_AsUnsignedLongLongMask(obj: *mut PyObject) -> c_ulonglong {
216+
with_vm::<PyResult<c_ulonglong>, _>(|vm| {
217+
let int = unsafe { &*obj }.to_owned().try_index(vm)?;
218+
Ok(int.as_u64_mask())
219+
})
220+
}
221+
222+
#[unsafe(no_mangle)]
223+
pub unsafe extern "C" fn PyLong_AsVoidPtr(obj: *mut PyObject) -> *mut c_void {
224+
with_vm(|vm| {
225+
let value = unsafe { &*obj }.to_owned().try_index(vm)?;
226+
227+
let unsigned: Result<usize, _> = value.as_bigint().try_into();
228+
if let Ok(v) = unsigned {
229+
return Ok(v as *mut c_void);
230+
}
231+
let signed: Result<isize, _> = value.as_bigint().try_into();
232+
if let Ok(v) = signed {
233+
return Ok((v as usize) as *mut c_void);
234+
}
235+
236+
Err(vm.new_overflow_error("int too large to convert to pointer"))
237+
})
238+
}
239+
53240
#[unsafe(no_mangle)]
54241
pub unsafe extern "C" fn PyLong_AsUnsignedLongLong(obj: *mut PyObject) -> c_ulonglong {
55242
with_vm::<PyResult<c_ulonglong>, _>(|vm| {
@@ -86,4 +273,13 @@ mod tests {
86273
assert_eq!(number.extract::<u64>().unwrap(), 123);
87274
})
88275
}
276+
277+
#[test]
278+
fn py_int_u128() {
279+
Python::attach(|py| {
280+
let value = 1u128 << 100;
281+
let number = PyInt::new(py, value);
282+
assert_eq!(number.extract::<u128>().unwrap(), value);
283+
})
284+
}
89285
}

crates/capi/src/util.rs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::PyObject;
22
use core::convert::Infallible;
3-
use core::ffi::{c_char, c_double, c_int, c_long, c_ulonglong, c_void};
3+
use core::ffi::{c_char, c_double, c_int, c_long, c_ulong, c_void};
44
use rustpython_vm::{PyObjectRef, PyRef, PyResult, VirtualMachine};
55

66
pub(crate) trait FfiResult<Output = Self> {
@@ -101,6 +101,31 @@ impl FfiResult<isize> for usize {
101101
}
102102
}
103103

104+
#[cfg(not(windows))]
105+
impl FfiResult for c_int {
106+
const ERR_VALUE: Self = -1;
107+
108+
fn into_output(self, _vm: &VirtualMachine) -> Self {
109+
self
110+
}
111+
}
112+
113+
impl FfiResult for usize {
114+
const ERR_VALUE: Self = Self::MAX;
115+
116+
fn into_output(self, _vm: &VirtualMachine) -> Self {
117+
self
118+
}
119+
}
120+
121+
impl FfiResult for isize {
122+
const ERR_VALUE: Self = -1;
123+
124+
fn into_output(self, _vm: &VirtualMachine) -> Self {
125+
self
126+
}
127+
}
128+
104129
impl FfiResult for c_long {
105130
const ERR_VALUE: Self = -1;
106131

@@ -109,7 +134,25 @@ impl FfiResult for c_long {
109134
}
110135
}
111136

112-
impl FfiResult for c_ulonglong {
137+
impl FfiResult for c_ulong {
138+
const ERR_VALUE: Self = Self::MAX;
139+
140+
fn into_output(self, _vm: &VirtualMachine) -> Self {
141+
self
142+
}
143+
}
144+
145+
#[cfg(windows)]
146+
impl FfiResult for core::ffi::c_longlong {
147+
const ERR_VALUE: Self = -1;
148+
149+
fn into_output(self, _vm: &VirtualMachine) -> Self {
150+
self
151+
}
152+
}
153+
154+
#[cfg(windows)]
155+
impl FfiResult for core::ffi::c_ulonglong {
113156
const ERR_VALUE: Self = Self::MAX;
114157

115158
fn into_output(self, _vm: &VirtualMachine) -> Self {
@@ -159,3 +202,39 @@ where
159202
)
160203
}
161204
}
205+
206+
#[cfg(test)]
207+
mod tests {
208+
use super::*;
209+
use core::fmt::Debug;
210+
use std::any::type_name;
211+
use std::ffi::{c_longlong, c_ulonglong};
212+
213+
#[test]
214+
fn ffi_result_err_value() {
215+
fn assert_error_value<T, Output>(value: Output)
216+
where
217+
T: FfiResult<Output> + 'static,
218+
Output: PartialEq + Debug,
219+
{
220+
assert_eq!(value, T::ERR_VALUE, "{}", type_name::<T>(),);
221+
}
222+
223+
assert_error_value::<(), _>(());
224+
assert_error_value::<(), c_int>(-1);
225+
226+
assert_error_value::<isize, _>(-1);
227+
assert_error_value::<usize, _>(usize::MAX);
228+
assert_error_value::<usize, isize>(-1);
229+
assert_error_value::<c_int, _>(-1); // i32
230+
assert_error_value::<c_long, _>(-1); //Windows i32, unix i64
231+
assert_error_value::<c_ulong, _>(c_ulong::MAX); // Windows u32, unix u64
232+
assert_error_value::<c_longlong, _>(-1); // i64
233+
assert_error_value::<c_ulonglong, _>(c_ulonglong::MAX); // u64
234+
assert_error_value::<c_double, _>(-1.0);
235+
assert_error_value::<bool, _>(-1);
236+
237+
assert_error_value::<PyResult<c_int>, _>(-1);
238+
assert_error_value::<PyResult<usize>, _>(usize::MAX);
239+
}
240+
}

crates/vm/src/builtins/int.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,12 +322,26 @@ impl PyInt {
322322
v.to_u32()
323323
.or_else(|| v.to_i32().map(|i| i as u32))
324324
.unwrap_or_else(|| {
325-
let mut out = 0u32;
326-
for digit in v.iter_u32_digits() {
327-
out = out.wrapping_shl(32) | digit;
325+
let out = v.iter_u32_digits().next().unwrap_or(0);
326+
match v.sign() {
327+
Sign::Minus => out.wrapping_neg(),
328+
_ => out,
328329
}
330+
})
331+
}
332+
333+
// _PyLong_AsUnsignedLongLongMask
334+
#[must_use]
335+
pub fn as_u64_mask(&self) -> u64 {
336+
let v = self.as_bigint();
337+
v.to_u64()
338+
.or_else(|| v.to_i64().map(|i| i as u64))
339+
.unwrap_or_else(|| {
340+
let mut digits = v.iter_u32_digits();
341+
let out = u64::from(digits.next().unwrap_or(0))
342+
| (u64::from(digits.next().unwrap_or(0)) << 32);
329343
match v.sign() {
330-
Sign::Minus => out * -1i32 as u32,
344+
Sign::Minus => out.wrapping_neg(),
331345
_ => out,
332346
}
333347
})

0 commit comments

Comments
 (0)