Skip to content

Commit 18e54a6

Browse files
Add PyLong_FromNativeBytes & PyLong_FromUnsignedNativeBytes
1 parent 82191bc commit 18e54a6

4 files changed

Lines changed: 97 additions & 1 deletion

File tree

.cspell.dict/cpython.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ argdefs
44
argtypes
55
asdl
66
asname
7+
ASNATIVEBYTES
78
atopen
89
atext
910
attro

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/capi/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ crate-type = ["cdylib", "rlib"]
1515
bitflags = { workspace = true }
1616
itertools = { workspace = true }
1717
libc = { workspace = true }
18+
malachite-bigint = { workspace = true }
1819
num-complex = { workspace = true }
1920
rustpython-vm = { workspace = true, features = ["threading", "compiler", "importlib", "host_env"] }
2021
rustpython-stdlib = {workspace = true, features = ["threading"] }

crates/capi/src/longobject.rs

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use crate::PyObject;
22
use crate::object::define_py_check;
33
use crate::pystate::with_vm;
4+
use bitflags::bitflags;
45
use core::ffi::{CStr, c_char, c_double, c_int, c_long, c_longlong, c_ulong, c_ulonglong, c_void};
6+
use malachite_bigint::{BigInt, Sign};
57
use rustpython_vm::builtins::{PyInt, try_bigint_to_f64, try_f64_to_bigint};
68
use rustpython_vm::common::int::bytes_to_int;
79
use rustpython_vm::protocol::handle_bytes_to_int_err;
8-
use rustpython_vm::{AsObject, PyResult};
10+
use rustpython_vm::{AsObject, PyResult, VirtualMachine};
911

1012
define_py_check!(fn PyLong_Check, types.int_type);
1113
define_py_check!(exact fn PyLong_CheckExact, types.int_type);
@@ -65,6 +67,97 @@ pub extern "C" fn PyLong_FromUInt64(value: u64) -> *mut PyObject {
6567
with_vm(|vm| vm.ctx.new_int(value))
6668
}
6769

70+
bitflags! {
71+
#[derive(Clone, Copy)]
72+
struct AsNativeBytesFlags: c_int {
73+
const BIG_ENDIAN = 0;
74+
const LITTLE_ENDIAN = 1;
75+
const NATIVE_ENDIAN = 3;
76+
const UNSIGNED_BUFFER = 4;
77+
const REJECT_NEGATIVE = 8;
78+
const ALLOW_INDEX = 16;
79+
}
80+
}
81+
82+
impl AsNativeBytesFlags {
83+
#[inline]
84+
fn is_little_endian(self) -> bool {
85+
if self.contains(Self::NATIVE_ENDIAN) {
86+
cfg!(target_endian = "little")
87+
} else {
88+
self.contains(Self::LITTLE_ENDIAN)
89+
}
90+
}
91+
92+
fn from_bits_or_default(vm: &VirtualMachine, raw_flags: c_int) -> PyResult<Self> {
93+
const PY_ASNATIVEBYTES_DEFAULTS: c_int = -1;
94+
if raw_flags == PY_ASNATIVEBYTES_DEFAULTS {
95+
return Ok(Self::default());
96+
};
97+
98+
let flags = Self::from_bits(raw_flags)
99+
.ok_or_else(|| vm.new_value_error("Invalid NativeBytes flags"))?;
100+
if flags.contains(Self::LITTLE_ENDIAN) & flags.contains(Self::NATIVE_ENDIAN) {
101+
Err(vm.new_value_error("Cannot specify both LITTLE_ENDIAN and NATIVE_ENDIAN"))
102+
} else {
103+
Ok(flags)
104+
}
105+
}
106+
}
107+
108+
impl Default for AsNativeBytesFlags {
109+
fn default() -> Self {
110+
Self::NATIVE_ENDIAN | Self::UNSIGNED_BUFFER
111+
}
112+
}
113+
114+
#[unsafe(no_mangle)]
115+
pub unsafe extern "C" fn PyLong_FromNativeBytes(
116+
buffer: *const c_void,
117+
n_bytes: usize,
118+
flags: c_int,
119+
) -> *mut PyObject {
120+
with_vm(|vm| {
121+
let flags = AsNativeBytesFlags::from_bits_or_default(vm, flags)?;
122+
let little_endian = flags.is_little_endian();
123+
let bytes = unsafe { core::slice::from_raw_parts(buffer.cast::<u8>(), n_bytes) };
124+
125+
let value = if flags.contains(AsNativeBytesFlags::UNSIGNED_BUFFER) {
126+
if little_endian {
127+
BigInt::from_bytes_le(Sign::Plus, bytes)
128+
} else {
129+
BigInt::from_bytes_be(Sign::Plus, bytes)
130+
}
131+
} else if little_endian {
132+
BigInt::from_signed_bytes_le(bytes)
133+
} else {
134+
BigInt::from_signed_bytes_be(bytes)
135+
};
136+
137+
Ok(vm.ctx.new_bigint(&value))
138+
})
139+
}
140+
141+
#[unsafe(no_mangle)]
142+
pub unsafe extern "C" fn PyLong_FromUnsignedNativeBytes(
143+
buffer: *const c_void,
144+
n_bytes: usize,
145+
flags: c_int,
146+
) -> *mut PyObject {
147+
with_vm(|vm| {
148+
let flags = AsNativeBytesFlags::from_bits_or_default(vm, flags)?;
149+
let bytes = unsafe { core::slice::from_raw_parts(buffer.cast::<u8>(), n_bytes) };
150+
151+
let value = if flags.is_little_endian() {
152+
BigInt::from_bytes_le(Sign::Plus, bytes)
153+
} else {
154+
BigInt::from_bytes_be(Sign::Plus, bytes)
155+
};
156+
157+
Ok(vm.ctx.new_bigint(&value))
158+
})
159+
}
160+
68161
#[unsafe(no_mangle)]
69162
pub extern "C" fn PyLong_FromVoidPtr(ptr: *mut c_void) -> *mut PyObject {
70163
with_vm(|vm| vm.ctx.new_int(ptr as usize))

0 commit comments

Comments
 (0)