mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
fix ctypes
This commit is contained in:
1 parent
4bf0bac52d
commit
79abbf0b29
8 files changed
+972
-472
No files matched your search
@@ -95,6 +95,7 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
pointer::PyCPointerType::make_class(ctx);
|
||||
structure::PyCStructType::make_class(ctx);
|
||||
union::PyCUnionType::make_class(ctx);
|
||||
function::PyCFuncPtrType::make_class(ctx);
|
||||
extend_module!(vm, &module, {
|
||||
"_CData" => PyCData::make_class(ctx),
|
||||
"_SimpleCData" => PyCSimple::make_class(ctx),
|
||||
@@ -385,12 +386,8 @@ pub(crate) mod _ctypes {
|
||||
#[pyattr]
|
||||
const RTLD_GLOBAL: i32 = 0;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[pyattr]
|
||||
const SIZEOF_TIME_T: usize = 8;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[pyattr]
|
||||
const SIZEOF_TIME_T: usize = 4;
|
||||
const SIZEOF_TIME_T: usize = std::mem::size_of::<libc::time_t>();
|
||||
|
||||
#[pyattr]
|
||||
const CTYPES_MAX_ARGCOUNT: usize = 1024;
|
||||
@@ -578,30 +575,42 @@ pub(crate) mod _ctypes {
|
||||
#[pyfunction(name = "dlopen")]
|
||||
fn load_library_unix(
|
||||
name: Option<crate::function::FsPath>,
|
||||
_load_flags: OptionalArg<i32>,
|
||||
load_flags: OptionalArg<i32>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<usize> {
|
||||
// TODO: audit functions first
|
||||
// TODO: load_flags
|
||||
// Default mode: RTLD_NOW | RTLD_LOCAL, always force RTLD_NOW
|
||||
let mode = load_flags.unwrap_or(libc::RTLD_NOW | libc::RTLD_LOCAL) | libc::RTLD_NOW;
|
||||
|
||||
match name {
|
||||
Some(name) => {
|
||||
let cache = library::libcache();
|
||||
let mut cache_write = cache.write();
|
||||
let os_str = name.as_os_str(vm)?;
|
||||
let (id, _) = cache_write.get_or_insert_lib(&*os_str, vm).map_err(|e| {
|
||||
// Include filename in error message for better diagnostics
|
||||
let name_str = os_str.to_string_lossy();
|
||||
vm.new_os_error(format!("{}: {}", name_str, e))
|
||||
})?;
|
||||
let (id, _) = cache_write
|
||||
.get_or_insert_lib_with_mode(&*os_str, mode, vm)
|
||||
.map_err(|e| {
|
||||
let name_str = os_str.to_string_lossy();
|
||||
vm.new_os_error(format!("{}: {}", name_str, e))
|
||||
})?;
|
||||
Ok(id)
|
||||
}
|
||||
None => {
|
||||
// If None, call libc::dlopen(null, mode) to get the current process handle
|
||||
let handle = unsafe { libc::dlopen(std::ptr::null(), libc::RTLD_NOW) };
|
||||
// dlopen(NULL, mode) to get the current process handle (for pythonapi)
|
||||
let handle = unsafe { libc::dlopen(std::ptr::null(), mode) };
|
||||
if handle.is_null() {
|
||||
return Err(vm.new_os_error("dlopen() error"));
|
||||
let err = unsafe { libc::dlerror() };
|
||||
let msg = if err.is_null() {
|
||||
"dlopen() error".to_string()
|
||||
} else {
|
||||
unsafe { std::ffi::CStr::from_ptr(err).to_string_lossy().into_owned() }
|
||||
};
|
||||
return Err(vm.new_os_error(msg));
|
||||
}
|
||||
Ok(handle as usize)
|
||||
// Add to library cache so symbol lookup works
|
||||
let cache = library::libcache();
|
||||
let mut cache_write = cache.write();
|
||||
let id = cache_write.insert_raw_handle(handle);
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -614,6 +623,48 @@ pub(crate) mod _ctypes {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[pyfunction]
|
||||
fn dlclose(handle: usize, _vm: &VirtualMachine) -> PyResult<()> {
|
||||
// Remove from cache, which triggers SharedLibrary drop.
|
||||
// libloading::Library calls dlclose automatically on Drop.
|
||||
let cache = library::libcache();
|
||||
let mut cache_write = cache.write();
|
||||
cache_write.drop_lib(handle);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[pyfunction]
|
||||
fn dlsym(
|
||||
handle: usize,
|
||||
name: crate::builtins::PyStrRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<usize> {
|
||||
let symbol_name = std::ffi::CString::new(name.as_str())
|
||||
.map_err(|_| vm.new_value_error("symbol name contains null byte"))?;
|
||||
|
||||
// Clear previous error
|
||||
unsafe { libc::dlerror() };
|
||||
|
||||
let ptr = unsafe { libc::dlsym(handle as *mut libc::c_void, symbol_name.as_ptr()) };
|
||||
|
||||
// Check for error via dlerror first
|
||||
let err = unsafe { libc::dlerror() };
|
||||
if !err.is_null() {
|
||||
let msg = unsafe { std::ffi::CStr::from_ptr(err).to_string_lossy().into_owned() };
|
||||
return Err(vm.new_os_error(msg));
|
||||
}
|
||||
|
||||
// Treat NULL symbol address as error
|
||||
// This handles cases like GNU IFUNCs that resolve to NULL
|
||||
if ptr.is_null() {
|
||||
return Err(vm.new_os_error(format!("symbol '{}' not found", name.as_str())));
|
||||
}
|
||||
|
||||
Ok(ptr as usize)
|
||||
}
|
||||
|
||||
#[pyfunction(name = "POINTER")]
|
||||
fn create_pointer_type(cls: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
use crate::builtins::PyStr;
|
||||
@@ -905,25 +956,24 @@ pub(crate) mod _ctypes {
|
||||
|
||||
#[pyfunction]
|
||||
fn get_errno() -> i32 {
|
||||
errno::errno().0
|
||||
super::function::get_errno_value()
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn set_errno(value: i32) {
|
||||
errno::set_errno(errno::Errno(value));
|
||||
fn set_errno(value: i32) -> i32 {
|
||||
super::function::set_errno_value(value)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[pyfunction]
|
||||
fn get_last_error() -> PyResult<u32> {
|
||||
Ok(unsafe { windows_sys::Win32::Foundation::GetLastError() })
|
||||
Ok(super::function::get_last_error_value())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[pyfunction]
|
||||
fn set_last_error(value: u32) -> PyResult<()> {
|
||||
unsafe { windows_sys::Win32::Foundation::SetLastError(value) };
|
||||
Ok(())
|
||||
fn set_last_error(value: u32) -> u32 {
|
||||
super::function::set_last_error_value(value)
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
@@ -1084,9 +1134,9 @@ pub(crate) mod _ctypes {
|
||||
ffi_args.push(Arg::new(val));
|
||||
}
|
||||
|
||||
let cif = Cif::new(arg_types, Type::isize());
|
||||
let cif = Cif::new(arg_types, Type::c_int());
|
||||
let code_ptr = CodePtr::from_ptr(func_addr as *const _);
|
||||
let result: isize = unsafe { cif.call(code_ptr, &ffi_args) };
|
||||
let result: libc::c_int = unsafe { cif.call(code_ptr, &ffi_args) };
|
||||
Ok(vm.ctx.new_int(result).into())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use super::StgInfo;
|
||||
use super::base::{CDATA_BUFFER_METHODS, PyCData};
|
||||
use super::type_info;
|
||||
use crate::{
|
||||
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
|
||||
atomic_func,
|
||||
builtins::{PyBytes, PyInt, PyList, PySlice, PyStr, PyType, PyTypeRef},
|
||||
builtins::{
|
||||
PyBytes, PyInt, PyList, PySlice, PyStr, PyType, PyTypeRef, genericalias::PyGenericAlias,
|
||||
},
|
||||
class::StaticType,
|
||||
function::{ArgBytesLike, FuncArgs, PySetterValue},
|
||||
protocol::{BufferDescriptor, PyBuffer, PyNumberMethods, PySequenceMethods},
|
||||
@@ -11,6 +14,19 @@ use crate::{
|
||||
};
|
||||
use num_traits::{Signed, ToPrimitive};
|
||||
|
||||
/// Get itemsize from a PEP 3118 format string
|
||||
/// Extracts the type code (last char after endianness prefix) and returns its size
|
||||
fn get_size_from_format(fmt: &str) -> usize {
|
||||
// Format is like "<f", ">q", etc. - strip endianness prefix and get type code
|
||||
let code = fmt
|
||||
.trim_start_matches(['<', '>', '@', '=', '!', '&'])
|
||||
.chars()
|
||||
.next()
|
||||
.map(|c| c.to_string());
|
||||
code.map(|c| type_info(&c).map(|t| t.size).unwrap_or(1))
|
||||
.unwrap_or(1)
|
||||
}
|
||||
|
||||
/// Creates array type for (element_type, length)
|
||||
/// Uses _array_type_cache to ensure identical calls return the same type object
|
||||
pub(super) fn array_type_from_ctype(
|
||||
@@ -444,6 +460,11 @@ impl AsSequence for PyCArray {
|
||||
with(Constructor, AsSequence, AsBuffer)
|
||||
)]
|
||||
impl PyCArray {
|
||||
#[pyclassmethod]
|
||||
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::from_args(cls, args, vm)
|
||||
}
|
||||
|
||||
fn int_to_bytes(i: &malachite_bigint::BigInt, size: usize) -> Vec<u8> {
|
||||
// Try unsigned first (handles values like 0xFFFFFFFF that overflow signed)
|
||||
// then fall back to signed (handles negative values)
|
||||
@@ -1056,19 +1077,30 @@ impl AsBuffer for PyCArray {
|
||||
.expect("PyCArray type must have StgInfo");
|
||||
let format = stg_info.format.clone();
|
||||
let shape = stg_info.shape.clone();
|
||||
let element_size = stg_info.element_size;
|
||||
|
||||
let desc = if let Some(fmt) = format
|
||||
&& !shape.is_empty()
|
||||
{
|
||||
// itemsize is the size of the base element type (item_info->size)
|
||||
// For empty arrays, we still need the element size, not 0
|
||||
let total_elements: usize = shape.iter().product();
|
||||
let has_zero_dim = shape.contains(&0);
|
||||
let itemsize = if total_elements > 0 && buffer_len > 0 {
|
||||
buffer_len / total_elements
|
||||
} else {
|
||||
// For empty arrays, get itemsize from format type code
|
||||
get_size_from_format(&fmt)
|
||||
};
|
||||
|
||||
// Build dim_desc from shape (C-contiguous: row-major order)
|
||||
// stride[i] = product(shape[i+1:]) * itemsize
|
||||
// For empty arrays (any dimension is 0), all strides are 0
|
||||
let mut dim_desc = Vec::with_capacity(shape.len());
|
||||
let mut stride = element_size as isize;
|
||||
let mut stride = itemsize as isize;
|
||||
|
||||
// Calculate strides from innermost to outermost dimension
|
||||
for &dim_size in shape.iter().rev() {
|
||||
dim_desc.push((dim_size, stride, 0));
|
||||
let current_stride = if has_zero_dim { 0 } else { stride };
|
||||
dim_desc.push((dim_size, current_stride, 0));
|
||||
stride *= dim_size as isize;
|
||||
}
|
||||
dim_desc.reverse();
|
||||
@@ -1076,7 +1108,7 @@ impl AsBuffer for PyCArray {
|
||||
BufferDescriptor {
|
||||
len: buffer_len,
|
||||
readonly: false,
|
||||
itemsize: element_size,
|
||||
itemsize,
|
||||
format: std::borrow::Cow::Owned(fmt),
|
||||
dim_desc,
|
||||
}
|
||||
|
||||
@@ -300,15 +300,27 @@ pub(super) fn get_field_format(
|
||||
big_endian: bool,
|
||||
vm: &VirtualMachine,
|
||||
) -> String {
|
||||
let endian_prefix = if big_endian { ">" } else { "<" };
|
||||
|
||||
// 1. Check StgInfo for format
|
||||
if let Some(type_obj) = field_type.downcast_ref::<PyType>()
|
||||
&& let Some(stg_info) = type_obj.stg_info_opt()
|
||||
&& let Some(fmt) = &stg_info.format
|
||||
{
|
||||
// Handle endian prefix for simple types
|
||||
if fmt.len() == 1 {
|
||||
let endian_prefix = if big_endian { ">" } else { "<" };
|
||||
return format!("{}{}", endian_prefix, fmt);
|
||||
// For structures (T{...}), arrays ((n)...), and pointers (&...), return as-is
|
||||
// These complex types have their own endianness markers inside
|
||||
if fmt.starts_with('T')
|
||||
|| fmt.starts_with('(')
|
||||
|| fmt.starts_with('&')
|
||||
|| fmt.starts_with("X{")
|
||||
{
|
||||
return fmt.clone();
|
||||
}
|
||||
|
||||
// For simple types, replace existing endian prefix with the correct one
|
||||
let base_fmt = fmt.trim_start_matches(['<', '>', '@', '=', '!']);
|
||||
if !base_fmt.is_empty() {
|
||||
return format!("{}{}", endian_prefix, base_fmt);
|
||||
}
|
||||
return fmt.clone();
|
||||
}
|
||||
@@ -318,8 +330,7 @@ pub(super) fn get_field_format(
|
||||
&& let Some(type_str) = type_attr.downcast_ref::<PyStr>()
|
||||
{
|
||||
let s = type_str.as_str();
|
||||
if s.len() == 1 {
|
||||
let endian_prefix = if big_endian { ">" } else { "<" };
|
||||
if !s.is_empty() {
|
||||
return format!("{}{}", endian_prefix, s);
|
||||
}
|
||||
return s.to_string();
|
||||
@@ -1168,29 +1179,30 @@ impl PyCData {
|
||||
.ok_or_else(|| vm.new_value_error("Invalid library handle"))?
|
||||
};
|
||||
|
||||
// Get symbol address using platform-specific API
|
||||
let symbol_name = std::ffi::CString::new(name.as_str())
|
||||
.map_err(|_| vm.new_value_error("Invalid symbol name"))?;
|
||||
// Look up the library in the cache and use lib.get() for symbol lookup
|
||||
let library_cache = super::library::libcache().read();
|
||||
let library = library_cache
|
||||
.get_lib(handle)
|
||||
.ok_or_else(|| vm.new_value_error("Library not found"))?;
|
||||
let inner_lib = library.lib.lock();
|
||||
|
||||
#[cfg(windows)]
|
||||
let ptr: *const u8 = unsafe {
|
||||
match windows_sys::Win32::System::LibraryLoader::GetProcAddress(
|
||||
handle as windows_sys::Win32::Foundation::HMODULE,
|
||||
symbol_name.as_ptr() as *const u8,
|
||||
) {
|
||||
Some(p) => p as *const u8,
|
||||
None => std::ptr::null(),
|
||||
let symbol_name_with_nul = format!("{}\0", name.as_str());
|
||||
let ptr: *const u8 = if let Some(lib) = &*inner_lib {
|
||||
unsafe {
|
||||
lib.get::<*const u8>(symbol_name_with_nul.as_bytes())
|
||||
.map(|sym| *sym)
|
||||
.map_err(|_| {
|
||||
vm.new_value_error(format!("symbol '{}' not found", name.as_str()))
|
||||
})?
|
||||
}
|
||||
} else {
|
||||
return Err(vm.new_value_error("Library closed"));
|
||||
};
|
||||
|
||||
#[cfg(not(windows))]
|
||||
let ptr: *const u8 =
|
||||
unsafe { libc::dlsym(handle as *mut libc::c_void, symbol_name.as_ptr()) as *const u8 };
|
||||
|
||||
// dlsym can return NULL for symbols that resolve to NULL (e.g., GNU IFUNC)
|
||||
// Treat NULL addresses as errors
|
||||
if ptr.is_null() {
|
||||
return Err(
|
||||
vm.new_value_error(format!("symbol '{}' not found in library", name.as_str()))
|
||||
);
|
||||
return Err(vm.new_value_error(format!("symbol '{}' not found", name.as_str())));
|
||||
}
|
||||
|
||||
// PyCData_AtAddress
|
||||
@@ -1593,7 +1605,7 @@ impl PyCField {
|
||||
/// PyCField_set
|
||||
#[pyslot]
|
||||
fn descr_set(
|
||||
zelf: &crate::PyObject,
|
||||
zelf: &PyObject,
|
||||
obj: PyObjectRef,
|
||||
value: PySetterValue<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
@@ -1804,7 +1816,7 @@ pub enum FfiArgValue {
|
||||
F64(f64),
|
||||
Pointer(usize),
|
||||
/// Pointer with owned data. The PyObjectRef keeps the pointed data alive.
|
||||
OwnedPointer(usize, #[allow(dead_code)] crate::PyObjectRef),
|
||||
OwnedPointer(usize, #[allow(dead_code)] PyObjectRef),
|
||||
}
|
||||
|
||||
impl FfiArgValue {
|
||||
@@ -2145,6 +2157,16 @@ pub(super) fn read_ptr_from_buffer(buffer: &[u8]) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a type is a "simple instance" (direct subclass of a simple type)
|
||||
/// Returns TRUE for c_int, c_void_p, etc. (simple types with _type_ attribute)
|
||||
/// Returns FALSE for Structure, Array, POINTER(T), etc.
|
||||
pub(super) fn is_simple_instance(typ: &Py<PyType>) -> bool {
|
||||
// _ctypes_simple_instance
|
||||
// Check if the type's metaclass is PyCSimpleType
|
||||
let metaclass = typ.class();
|
||||
metaclass.fast_issubclass(super::simple::PyCSimpleType::static_type())
|
||||
}
|
||||
|
||||
/// Set or initialize StgInfo on a type
|
||||
pub(super) fn set_or_init_stginfo(type_ref: &PyType, stg_info: StgInfo) {
|
||||
if type_ref.init_type_data(stg_info.clone()).is_err()
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -2,12 +2,14 @@ use crate::VirtualMachine;
|
||||
use libloading::Library;
|
||||
use rustpython_common::lock::{PyMutex, PyRwLock};
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{OsStr, c_void};
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::ptr::null;
|
||||
|
||||
pub(super) struct SharedLibrary {
|
||||
pub(super) lib: PyMutex<Option<Library>>,
|
||||
#[cfg(unix)]
|
||||
use libloading::os::unix::Library as UnixLibrary;
|
||||
|
||||
pub struct SharedLibrary {
|
||||
pub(crate) lib: PyMutex<Option<Library>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for SharedLibrary {
|
||||
@@ -17,18 +19,44 @@ impl fmt::Debug for SharedLibrary {
|
||||
}
|
||||
|
||||
impl SharedLibrary {
|
||||
fn new(name: impl AsRef<OsStr>) -> Result<SharedLibrary, libloading::Error> {
|
||||
#[cfg(windows)]
|
||||
pub fn new(name: impl AsRef<OsStr>) -> Result<SharedLibrary, libloading::Error> {
|
||||
Ok(SharedLibrary {
|
||||
lib: PyMutex::new(unsafe { Some(Library::new(name.as_ref())?) }),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_pointer(&self) -> usize {
|
||||
#[cfg(unix)]
|
||||
pub fn new_with_mode(
|
||||
name: impl AsRef<OsStr>,
|
||||
mode: i32,
|
||||
) -> Result<SharedLibrary, libloading::Error> {
|
||||
Ok(SharedLibrary {
|
||||
lib: PyMutex::new(Some(unsafe {
|
||||
UnixLibrary::open(Some(name.as_ref()), mode)?.into()
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a SharedLibrary from a raw dlopen handle (for pythonapi / dlopen(NULL))
|
||||
#[cfg(unix)]
|
||||
pub fn from_raw_handle(handle: *mut libc::c_void) -> SharedLibrary {
|
||||
SharedLibrary {
|
||||
lib: PyMutex::new(Some(unsafe { UnixLibrary::from_raw(handle).into() })),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the underlying OS handle (HMODULE on Windows, dlopen handle on Unix)
|
||||
pub fn get_pointer(&self) -> usize {
|
||||
let lib_lock = self.lib.lock();
|
||||
if let Some(l) = &*lib_lock {
|
||||
l as *const Library as usize
|
||||
// libloading::Library internally stores the OS handle directly
|
||||
// On Windows: HMODULE (*mut c_void)
|
||||
// On Unix: *mut c_void from dlopen
|
||||
// We use transmute_copy to read the handle without consuming the Library
|
||||
unsafe { std::mem::transmute_copy::<Library, usize>(l) }
|
||||
} else {
|
||||
null::<c_void>() as usize
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,16 +64,6 @@ impl SharedLibrary {
|
||||
let lib_lock = self.lib.lock();
|
||||
lib_lock.is_none()
|
||||
}
|
||||
|
||||
fn close(&self) {
|
||||
*self.lib.lock() = None;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SharedLibrary {
|
||||
fn drop(&mut self) {
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct ExternalLibs {
|
||||
@@ -63,6 +81,7 @@ impl ExternalLibs {
|
||||
self.libraries.get(&key)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn get_or_insert_lib(
|
||||
&mut self,
|
||||
library_path: impl AsRef<OsStr>,
|
||||
@@ -71,20 +90,53 @@ impl ExternalLibs {
|
||||
let new_lib = SharedLibrary::new(library_path)?;
|
||||
let key = new_lib.get_pointer();
|
||||
|
||||
match self.libraries.get(&key) {
|
||||
Some(l) => {
|
||||
if l.is_closed() {
|
||||
self.libraries.insert(key, new_lib);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.libraries.insert(key, new_lib);
|
||||
}
|
||||
};
|
||||
// Check if library already exists and is not closed
|
||||
let should_use_cached = self.libraries.get(&key).is_some_and(|l| !l.is_closed());
|
||||
|
||||
if should_use_cached {
|
||||
// new_lib will be dropped, calling FreeLibrary (decrements refcount)
|
||||
// But library stays loaded because cached version maintains refcount
|
||||
drop(new_lib);
|
||||
return Ok((key, self.libraries.get(&key).expect("just checked")));
|
||||
}
|
||||
|
||||
self.libraries.insert(key, new_lib);
|
||||
Ok((key, self.libraries.get(&key).expect("just inserted")))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn get_or_insert_lib_with_mode(
|
||||
&mut self,
|
||||
library_path: impl AsRef<OsStr>,
|
||||
mode: i32,
|
||||
_vm: &VirtualMachine,
|
||||
) -> Result<(usize, &SharedLibrary), libloading::Error> {
|
||||
let new_lib = SharedLibrary::new_with_mode(library_path, mode)?;
|
||||
let key = new_lib.get_pointer();
|
||||
|
||||
// Check if library already exists and is not closed
|
||||
let should_use_cached = self.libraries.get(&key).is_some_and(|l| !l.is_closed());
|
||||
|
||||
if should_use_cached {
|
||||
// new_lib will be dropped, calling dlclose (decrements refcount)
|
||||
// But library stays loaded because cached version maintains refcount
|
||||
drop(new_lib);
|
||||
return Ok((key, self.libraries.get(&key).expect("just checked")));
|
||||
}
|
||||
|
||||
self.libraries.insert(key, new_lib);
|
||||
Ok((key, self.libraries.get(&key).expect("just inserted")))
|
||||
}
|
||||
|
||||
/// Insert a raw dlopen handle into the cache (for pythonapi / dlopen(NULL))
|
||||
#[cfg(unix)]
|
||||
pub fn insert_raw_handle(&mut self, handle: *mut libc::c_void) -> usize {
|
||||
let shared_lib = SharedLibrary::from_raw_handle(handle);
|
||||
let key = handle as usize;
|
||||
self.libraries.insert(key, shared_lib);
|
||||
key
|
||||
}
|
||||
|
||||
pub fn drop_lib(&mut self, key: usize) {
|
||||
self.libraries.remove(&key);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::base::CDATA_BUFFER_METHODS;
|
||||
use super::{PyCArray, PyCData, PyCSimple, PyCStructure, StgInfo, StgInfoFlags};
|
||||
use crate::protocol::PyNumberMethods;
|
||||
use crate::types::{AsNumber, Constructor, Initializer};
|
||||
use crate::protocol::{BufferDescriptor, PyBuffer, PyNumberMethods};
|
||||
use crate::types::{AsBuffer, AsNumber, Constructor, Initializer};
|
||||
use crate::{
|
||||
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyBytes, PyInt, PyList, PySlice, PyStr, PyType, PyTypeRef},
|
||||
@@ -8,6 +9,7 @@ use crate::{
|
||||
function::{FuncArgs, OptionalArg},
|
||||
};
|
||||
use num_traits::ToPrimitive;
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[pyclass(name = "PyCPointerType", base = PyType, module = "_ctypes")]
|
||||
#[derive(Debug)]
|
||||
@@ -42,11 +44,19 @@ impl Initializer for PyCPointerType {
|
||||
stg_info.length = 1;
|
||||
stg_info.flags |= StgInfoFlags::TYPEFLAG_ISPOINTER;
|
||||
|
||||
// Set format string: "&<element_format>"
|
||||
if let Some(ref proto) = stg_info.proto {
|
||||
let item_info = proto.stg_info_opt().expect("proto has StgInfo");
|
||||
// Set format string: "&<element_format>" or "&(shape)<element_format>" for arrays
|
||||
if let Some(ref proto) = stg_info.proto
|
||||
&& let Some(item_info) = proto.stg_info_opt()
|
||||
{
|
||||
let current_format = item_info.format.as_deref().unwrap_or("B");
|
||||
stg_info.format = Some(format!("&{}", current_format));
|
||||
// Include shape for array types in the pointer format
|
||||
let shape_str = if !item_info.shape.is_empty() {
|
||||
let dims: Vec<String> = item_info.shape.iter().map(|d| d.to_string()).collect();
|
||||
format!("({})", dims.join(","))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
stg_info.format = Some(format!("&{}{}", shape_str, current_format));
|
||||
}
|
||||
|
||||
let _ = new_type.init_type_data(stg_info);
|
||||
@@ -69,6 +79,15 @@ impl PyCPointerType {
|
||||
return Ok(value);
|
||||
}
|
||||
|
||||
// 1.5 CArgObject (from byref()) - check if underlying obj is instance of _type_
|
||||
if let Some(carg) = value.downcast_ref::<super::_ctypes::CArgObject>()
|
||||
&& let Ok(type_attr) = cls.as_object().get_attr("_type_", vm)
|
||||
&& let Ok(type_ref) = type_attr.downcast::<PyType>()
|
||||
&& carg.obj.is_instance(type_ref.as_object(), vm)?
|
||||
{
|
||||
return Ok(value);
|
||||
}
|
||||
|
||||
// 2. If already an instance of the requested type, return it
|
||||
if value.is_instance(cls.as_object(), vm)? {
|
||||
return Ok(value);
|
||||
@@ -149,10 +168,17 @@ impl PyCPointerType {
|
||||
if let Some(mut stg_info) = zelf.get_type_data_mut::<StgInfo>() {
|
||||
stg_info.proto = Some(typ_type.clone());
|
||||
|
||||
// Update format string: "&<element_format>"
|
||||
// Update format string: "&<element_format>" or "&(shape)<element_format>" for arrays
|
||||
let item_info = typ_type.stg_info_opt().expect("proto has StgInfo");
|
||||
let current_format = item_info.format.as_deref().unwrap_or("B");
|
||||
stg_info.format = Some(format!("&{}", current_format));
|
||||
// Include shape for array types in the pointer format
|
||||
let shape_str = if !item_info.shape.is_empty() {
|
||||
let dims: Vec<String> = item_info.shape.iter().map(|d| d.to_string()).collect();
|
||||
format!("({})", dims.join(","))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
stg_info.format = Some(format!("&{}{}", shape_str, current_format));
|
||||
}
|
||||
|
||||
// 4. Set _type_ attribute on the pointer type
|
||||
@@ -233,7 +259,10 @@ impl Initializer for PyCPointer {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(flags(BASETYPE, IMMUTABLETYPE), with(Constructor, Initializer))]
|
||||
#[pyclass(
|
||||
flags(BASETYPE, IMMUTABLETYPE),
|
||||
with(Constructor, Initializer, AsBuffer)
|
||||
)]
|
||||
impl PyCPointer {
|
||||
/// Get the pointer value stored in buffer as usize
|
||||
pub fn get_ptr_value(&self) -> usize {
|
||||
@@ -605,20 +634,47 @@ impl PyCPointer {
|
||||
unsafe {
|
||||
let ptr = addr as *const u8;
|
||||
match type_code {
|
||||
// Single-byte types don't need read_unaligned
|
||||
Some("c") => Ok(vm.ctx.new_bytes(vec![*ptr]).into()),
|
||||
Some("b") => Ok(vm.ctx.new_int(*(ptr as *const i8) as i32).into()),
|
||||
Some("b") => Ok(vm.ctx.new_int(*ptr as i8 as i32).into()),
|
||||
Some("B") => Ok(vm.ctx.new_int(*ptr as i32).into()),
|
||||
Some("h") => Ok(vm.ctx.new_int(*(ptr as *const i16) as i32).into()),
|
||||
Some("H") => Ok(vm.ctx.new_int(*(ptr as *const u16) as i32).into()),
|
||||
Some("i") | Some("l") => Ok(vm.ctx.new_int(*(ptr as *const i32)).into()),
|
||||
Some("I") | Some("L") => Ok(vm.ctx.new_int(*(ptr as *const u32)).into()),
|
||||
Some("q") => Ok(vm.ctx.new_int(*(ptr as *const i64)).into()),
|
||||
Some("Q") => Ok(vm.ctx.new_int(*(ptr as *const u64)).into()),
|
||||
Some("f") => Ok(vm.ctx.new_float(*(ptr as *const f32) as f64).into()),
|
||||
Some("d") | Some("g") => Ok(vm.ctx.new_float(*(ptr as *const f64)).into()),
|
||||
Some("P") | Some("z") | Some("Z") => {
|
||||
Ok(vm.ctx.new_int(*(ptr as *const usize)).into())
|
||||
}
|
||||
// Multi-byte types need read_unaligned for safety on strict-alignment architectures
|
||||
Some("h") => Ok(vm
|
||||
.ctx
|
||||
.new_int(std::ptr::read_unaligned(ptr as *const i16) as i32)
|
||||
.into()),
|
||||
Some("H") => Ok(vm
|
||||
.ctx
|
||||
.new_int(std::ptr::read_unaligned(ptr as *const u16) as i32)
|
||||
.into()),
|
||||
Some("i") | Some("l") => Ok(vm
|
||||
.ctx
|
||||
.new_int(std::ptr::read_unaligned(ptr as *const i32))
|
||||
.into()),
|
||||
Some("I") | Some("L") => Ok(vm
|
||||
.ctx
|
||||
.new_int(std::ptr::read_unaligned(ptr as *const u32))
|
||||
.into()),
|
||||
Some("q") => Ok(vm
|
||||
.ctx
|
||||
.new_int(std::ptr::read_unaligned(ptr as *const i64))
|
||||
.into()),
|
||||
Some("Q") => Ok(vm
|
||||
.ctx
|
||||
.new_int(std::ptr::read_unaligned(ptr as *const u64))
|
||||
.into()),
|
||||
Some("f") => Ok(vm
|
||||
.ctx
|
||||
.new_float(std::ptr::read_unaligned(ptr as *const f32) as f64)
|
||||
.into()),
|
||||
Some("d") | Some("g") => Ok(vm
|
||||
.ctx
|
||||
.new_float(std::ptr::read_unaligned(ptr as *const f64))
|
||||
.into()),
|
||||
Some("P") | Some("z") | Some("Z") => Ok(vm
|
||||
.ctx
|
||||
.new_int(std::ptr::read_unaligned(ptr as *const usize))
|
||||
.into()),
|
||||
_ => {
|
||||
// Default: read as bytes
|
||||
let bytes = std::slice::from_raw_parts(ptr, size).to_vec();
|
||||
@@ -652,27 +708,37 @@ impl PyCPointer {
|
||||
"bytes/string or integer address expected".to_owned(),
|
||||
));
|
||||
};
|
||||
*(ptr as *mut usize) = ptr_val;
|
||||
std::ptr::write_unaligned(ptr as *mut usize, ptr_val);
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Try to get value as integer
|
||||
// Use write_unaligned for safety on strict-alignment architectures
|
||||
if let Ok(int_val) = value.try_int(vm) {
|
||||
let i = int_val.as_bigint();
|
||||
match size {
|
||||
1 => {
|
||||
*ptr = i.to_u8().unwrap_or(0);
|
||||
*ptr = i.to_u8().expect("int too large");
|
||||
}
|
||||
2 => {
|
||||
*(ptr as *mut i16) = i.to_i16().unwrap_or(0);
|
||||
std::ptr::write_unaligned(
|
||||
ptr as *mut i16,
|
||||
i.to_i16().expect("int too large"),
|
||||
);
|
||||
}
|
||||
4 => {
|
||||
*(ptr as *mut i32) = i.to_i32().unwrap_or(0);
|
||||
std::ptr::write_unaligned(
|
||||
ptr as *mut i32,
|
||||
i.to_i32().expect("int too large"),
|
||||
);
|
||||
}
|
||||
8 => {
|
||||
*(ptr as *mut i64) = i.to_i64().unwrap_or(0);
|
||||
std::ptr::write_unaligned(
|
||||
ptr as *mut i64,
|
||||
i.to_i64().expect("int too large"),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
let bytes = i.to_signed_bytes_le();
|
||||
@@ -688,10 +754,10 @@ impl PyCPointer {
|
||||
let f = float_val.to_f64();
|
||||
match size {
|
||||
4 => {
|
||||
*(ptr as *mut f32) = f as f32;
|
||||
std::ptr::write_unaligned(ptr as *mut f32, f as f32);
|
||||
}
|
||||
8 => {
|
||||
*(ptr as *mut f64) = f;
|
||||
std::ptr::write_unaligned(ptr as *mut f64, f);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -712,3 +778,28 @@ impl PyCPointer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBuffer for PyCPointer {
|
||||
fn as_buffer(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyBuffer> {
|
||||
let stg_info = zelf
|
||||
.class()
|
||||
.stg_info_opt()
|
||||
.expect("PyCPointer type must have StgInfo");
|
||||
let format = stg_info
|
||||
.format
|
||||
.clone()
|
||||
.map(Cow::Owned)
|
||||
.unwrap_or(Cow::Borrowed("&B"));
|
||||
let itemsize = stg_info.size;
|
||||
// Pointer types are scalars with ndim=0, shape=()
|
||||
let desc = BufferDescriptor {
|
||||
len: itemsize,
|
||||
readonly: false,
|
||||
itemsize,
|
||||
format,
|
||||
dim_desc: vec![],
|
||||
};
|
||||
let buf = PyBuffer::new(zelf.to_owned().into(), desc, &CDATA_BUFFER_METHODS);
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,47 @@ use crate::protocol::{BufferDescriptor, PyBuffer, PyNumberMethods};
|
||||
use crate::types::{AsBuffer, AsNumber, Constructor, Initializer, Representable};
|
||||
use crate::{AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine};
|
||||
use num_traits::ToPrimitive;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Valid type codes for ctypes simple types
|
||||
// spell-checker: disable-next-line
|
||||
pub(super) const SIMPLE_TYPE_CHARS: &str = "cbBhHiIlLdfuzZqQPXOv?g";
|
||||
|
||||
/// Convert ctypes type code to PEP 3118 format code.
|
||||
/// Some ctypes codes need to be mapped to standard-size codes based on platform.
|
||||
/// _ctypes_alloc_format_string_for_type
|
||||
fn ctypes_code_to_pep3118(code: char) -> char {
|
||||
match code {
|
||||
// c_int: map based on sizeof(int)
|
||||
'i' if std::mem::size_of::<std::ffi::c_int>() == 2 => 'h',
|
||||
'i' if std::mem::size_of::<std::ffi::c_int>() == 4 => 'i',
|
||||
'i' if std::mem::size_of::<std::ffi::c_int>() == 8 => 'q',
|
||||
'I' if std::mem::size_of::<std::ffi::c_int>() == 2 => 'H',
|
||||
'I' if std::mem::size_of::<std::ffi::c_int>() == 4 => 'I',
|
||||
'I' if std::mem::size_of::<std::ffi::c_int>() == 8 => 'Q',
|
||||
// c_long: map based on sizeof(long)
|
||||
'l' if std::mem::size_of::<std::ffi::c_long>() == 4 => 'l',
|
||||
'l' if std::mem::size_of::<std::ffi::c_long>() == 8 => 'q',
|
||||
'L' if std::mem::size_of::<std::ffi::c_long>() == 4 => 'L',
|
||||
'L' if std::mem::size_of::<std::ffi::c_long>() == 8 => 'Q',
|
||||
// c_bool: map based on sizeof(bool) - typically 1 byte on all platforms
|
||||
'?' if std::mem::size_of::<bool>() == 1 => '?',
|
||||
'?' if std::mem::size_of::<bool>() == 2 => 'H',
|
||||
'?' if std::mem::size_of::<bool>() == 4 => 'L',
|
||||
'?' if std::mem::size_of::<bool>() == 8 => 'Q',
|
||||
// Default: use the same code
|
||||
_ => code,
|
||||
}
|
||||
}
|
||||
|
||||
/// _ctypes_alloc_format_string_for_type
|
||||
fn alloc_format_string_for_type(code: char, big_endian: bool) -> String {
|
||||
let prefix = if big_endian { ">" } else { "<" };
|
||||
let pep_code = ctypes_code_to_pep3118(code);
|
||||
format!("{}{}", prefix, pep_code)
|
||||
}
|
||||
|
||||
/// Create a new simple type instance from a class
|
||||
fn new_simple_type(
|
||||
cls: Either<&PyObject, &Py<PyType>>,
|
||||
@@ -165,6 +200,20 @@ fn set_primitive(_type_: &str, value: &PyObject, vm: &VirtualMachine) -> PyResul
|
||||
}
|
||||
// O_set: py_object accepts any Python object
|
||||
"O" => Ok(value.to_owned()),
|
||||
// X_set: BSTR - same as Z (c_wchar_p), accepts None, int, or str
|
||||
"X" => {
|
||||
if value.is(&vm.ctx.none)
|
||||
|| value.downcast_ref_if_exact::<PyInt>(vm).is_some()
|
||||
|| value.downcast_ref_if_exact::<PyStr>(vm).is_some()
|
||||
{
|
||||
Ok(value.to_owned())
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(
|
||||
"unicode string or integer address expected instead of {} instance",
|
||||
value.class().name()
|
||||
)))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// "P"
|
||||
if value.downcast_ref_if_exact::<PyInt>(vm).is_some()
|
||||
@@ -313,7 +362,7 @@ impl PyCSimpleType {
|
||||
.to_pyobject(vm));
|
||||
}
|
||||
// 2. Array/Pointer with c_wchar element type
|
||||
if is_cwchar_array_or_pointer(&value, vm) {
|
||||
if is_cwchar_array_or_pointer(&value, vm)? {
|
||||
return Ok(value);
|
||||
}
|
||||
// 3. CArgObject (byref(c_wchar(...)))
|
||||
@@ -521,13 +570,10 @@ impl Initializer for PyCSimpleType {
|
||||
let mut stg_info = StgInfo::new(size, align);
|
||||
|
||||
// Set format for PEP 3118 buffer protocol
|
||||
// Format is endian prefix + type code (e.g., "<i" for little-endian int)
|
||||
let endian_prefix = if cfg!(target_endian = "little") {
|
||||
"<"
|
||||
} else {
|
||||
">"
|
||||
};
|
||||
stg_info.format = Some(format!("{}{}", endian_prefix, type_str));
|
||||
stg_info.format = Some(alloc_format_string_for_type(
|
||||
type_str.chars().next().unwrap_or('?'),
|
||||
cfg!(target_endian = "big"),
|
||||
));
|
||||
stg_info.paramfunc = super::base::ParamFunc::Simple;
|
||||
|
||||
// Set TYPEFLAG_ISPOINTER for pointer types: z (c_char_p), Z (c_wchar_p),
|
||||
@@ -619,13 +665,14 @@ fn create_swapped_types(
|
||||
swapped_type.set_attr("_swappedbytes_", vm.ctx.none(), vm)?;
|
||||
|
||||
// Update swapped type's StgInfo format to use opposite endian prefix
|
||||
// Native uses '<' on little-endian, '>' on big-endian
|
||||
// Swapped uses the opposite
|
||||
if let Ok(swapped_type_ref) = swapped_type.clone().downcast::<PyType>()
|
||||
&& let Some(mut sw_stg) = swapped_type_ref.get_type_data_mut::<StgInfo>()
|
||||
{
|
||||
let swapped_prefix = if is_little_endian { ">" } else { "<" };
|
||||
sw_stg.format = Some(format!("{}{}", swapped_prefix, type_str));
|
||||
// Swapped: little-endian system uses big-endian prefix and vice versa
|
||||
sw_stg.format = Some(alloc_format_string_for_type(
|
||||
type_str.chars().next().unwrap_or('?'),
|
||||
is_little_endian,
|
||||
));
|
||||
}
|
||||
|
||||
// Set attributes based on system byte order
|
||||
@@ -734,63 +781,56 @@ fn value_to_bytes_endian(
|
||||
}
|
||||
"b" => {
|
||||
// c_byte - signed char (1 byte)
|
||||
// PyLong_AsLongMask pattern: wrapping for overflow values
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_i128().unwrap_or(0) as i8;
|
||||
let v = int_val.as_bigint().to_i128().expect("int too large") as i8;
|
||||
return vec![v as u8];
|
||||
}
|
||||
vec![0]
|
||||
}
|
||||
"B" => {
|
||||
// c_ubyte - unsigned char (1 byte)
|
||||
// PyLong_AsUnsignedLongMask: wrapping for negative values
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_i128().map(|n| n as u8).unwrap_or(0);
|
||||
let v = int_val.as_bigint().to_i128().expect("int too large") as u8;
|
||||
return vec![v];
|
||||
}
|
||||
vec![0]
|
||||
}
|
||||
"h" => {
|
||||
// c_short (2 bytes)
|
||||
// PyLong_AsLongMask pattern: wrapping for overflow values
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_i128().unwrap_or(0) as i16;
|
||||
let v = int_val.as_bigint().to_i128().expect("int too large") as i16;
|
||||
return to_bytes!(v);
|
||||
}
|
||||
vec![0; 2]
|
||||
}
|
||||
"H" => {
|
||||
// c_ushort (2 bytes)
|
||||
// PyLong_AsUnsignedLongMask: wrapping for negative values
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_i128().map(|n| n as u16).unwrap_or(0);
|
||||
let v = int_val.as_bigint().to_i128().expect("int too large") as u16;
|
||||
return to_bytes!(v);
|
||||
}
|
||||
vec![0; 2]
|
||||
}
|
||||
"i" => {
|
||||
// c_int (4 bytes)
|
||||
// PyLong_AsLongMask pattern: wrapping for overflow values
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_i128().unwrap_or(0) as i32;
|
||||
let v = int_val.as_bigint().to_i128().expect("int too large") as i32;
|
||||
return to_bytes!(v);
|
||||
}
|
||||
vec![0; 4]
|
||||
}
|
||||
"I" => {
|
||||
// c_uint (4 bytes)
|
||||
// PyLong_AsUnsignedLongMask: wrapping for negative values
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_i128().map(|n| n as u32).unwrap_or(0);
|
||||
let v = int_val.as_bigint().to_i128().expect("int too large") as u32;
|
||||
return to_bytes!(v);
|
||||
}
|
||||
vec![0; 4]
|
||||
}
|
||||
"l" => {
|
||||
// c_long (platform dependent)
|
||||
// PyLong_AsLongMask pattern: wrapping for overflow values
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_i128().unwrap_or(0) as libc::c_long;
|
||||
let v = int_val.as_bigint().to_i128().expect("int too large") as libc::c_long;
|
||||
return to_bytes!(v);
|
||||
}
|
||||
const SIZE: usize = std::mem::size_of::<libc::c_long>();
|
||||
@@ -798,13 +838,8 @@ fn value_to_bytes_endian(
|
||||
}
|
||||
"L" => {
|
||||
// c_ulong (platform dependent)
|
||||
// PyLong_AsUnsignedLongMask: wrapping for negative values
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val
|
||||
.as_bigint()
|
||||
.to_i128()
|
||||
.map(|n| n as libc::c_ulong)
|
||||
.unwrap_or(0);
|
||||
let v = int_val.as_bigint().to_i128().expect("int too large") as libc::c_ulong;
|
||||
return to_bytes!(v);
|
||||
}
|
||||
const SIZE: usize = std::mem::size_of::<libc::c_ulong>();
|
||||
@@ -812,18 +847,16 @@ fn value_to_bytes_endian(
|
||||
}
|
||||
"q" => {
|
||||
// c_longlong (8 bytes)
|
||||
// PyLong_AsLongMask pattern: wrapping for overflow values
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_i128().unwrap_or(0) as i64;
|
||||
let v = int_val.as_bigint().to_i128().expect("int too large") as i64;
|
||||
return to_bytes!(v);
|
||||
}
|
||||
vec![0; 8]
|
||||
}
|
||||
"Q" => {
|
||||
// c_ulonglong (8 bytes)
|
||||
// PyLong_AsUnsignedLongLongMask: wrapping for negative values
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_i128().map(|n| n as u64).unwrap_or(0);
|
||||
let v = int_val.as_bigint().to_i128().expect("int too large") as u64;
|
||||
return to_bytes!(v);
|
||||
}
|
||||
vec![0; 8]
|
||||
@@ -899,7 +932,10 @@ fn value_to_bytes_endian(
|
||||
"P" => {
|
||||
// c_void_p - pointer type (platform pointer size)
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_usize().unwrap_or(0);
|
||||
let v = int_val
|
||||
.as_bigint()
|
||||
.to_usize()
|
||||
.expect("int too large for pointer");
|
||||
return to_bytes!(v);
|
||||
}
|
||||
vec![0; std::mem::size_of::<usize>()]
|
||||
@@ -908,7 +944,10 @@ fn value_to_bytes_endian(
|
||||
// c_char_p - pointer to char (stores pointer value from int)
|
||||
// PyBytes case is handled in slot_new/set_value with make_z_buffer()
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_usize().unwrap_or(0);
|
||||
let v = int_val
|
||||
.as_bigint()
|
||||
.to_usize()
|
||||
.expect("int too large for pointer");
|
||||
return to_bytes!(v);
|
||||
}
|
||||
vec![0; std::mem::size_of::<usize>()]
|
||||
@@ -917,7 +956,10 @@ fn value_to_bytes_endian(
|
||||
// c_wchar_p - pointer to wchar_t (stores pointer value from int)
|
||||
// PyStr case is handled in slot_new/set_value with make_wchar_buffer()
|
||||
if let Ok(int_val) = value.try_index(vm) {
|
||||
let v = int_val.as_bigint().to_usize().unwrap_or(0);
|
||||
let v = int_val
|
||||
.as_bigint()
|
||||
.to_usize()
|
||||
.expect("int too large for pointer");
|
||||
return to_bytes!(v);
|
||||
}
|
||||
vec![0; std::mem::size_of::<usize>()]
|
||||
@@ -939,7 +981,7 @@ fn is_cchar_array_or_pointer(value: &PyObject, vm: &VirtualMachine) -> bool {
|
||||
if let Some(arr) = value.downcast_ref::<PyCArray>()
|
||||
&& let Some(info) = arr.class().stg_info_opt()
|
||||
&& let Some(ref elem_type) = info.element_type
|
||||
&& let Some(elem_code) = elem_type.class().type_code(vm)
|
||||
&& let Some(elem_code) = elem_type.type_code(vm)
|
||||
{
|
||||
return elem_code == "c";
|
||||
}
|
||||
@@ -947,7 +989,7 @@ fn is_cchar_array_or_pointer(value: &PyObject, vm: &VirtualMachine) -> bool {
|
||||
if let Some(ptr) = value.downcast_ref::<PyCPointer>()
|
||||
&& let Some(info) = ptr.class().stg_info_opt()
|
||||
&& let Some(ref proto) = info.proto
|
||||
&& let Some(proto_code) = proto.class().type_code(vm)
|
||||
&& let Some(proto_code) = proto.type_code(vm)
|
||||
{
|
||||
return proto_code == "c";
|
||||
}
|
||||
@@ -955,25 +997,25 @@ fn is_cchar_array_or_pointer(value: &PyObject, vm: &VirtualMachine) -> bool {
|
||||
}
|
||||
|
||||
/// Check if value is a c_wchar array or pointer(c_wchar)
|
||||
fn is_cwchar_array_or_pointer(value: &PyObject, vm: &VirtualMachine) -> bool {
|
||||
fn is_cwchar_array_or_pointer(value: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
// Check Array with c_wchar element type
|
||||
if let Some(arr) = value.downcast_ref::<PyCArray>() {
|
||||
let info = arr.class().stg_info_opt().expect("array has StgInfo");
|
||||
let info = arr.class().stg_info(vm)?;
|
||||
let elem_type = info.element_type.as_ref().expect("array has element_type");
|
||||
if let Some(elem_code) = elem_type.class().type_code(vm) {
|
||||
return elem_code == "u";
|
||||
if let Some(elem_code) = elem_type.type_code(vm) {
|
||||
return Ok(elem_code == "u");
|
||||
}
|
||||
}
|
||||
// Check Pointer to c_wchar
|
||||
if let Some(ptr) = value.downcast_ref::<PyCPointer>() {
|
||||
let info = ptr.class().stg_info_opt().expect("pointer has StgInfo");
|
||||
let info = ptr.class().stg_info(vm)?;
|
||||
if let Some(ref proto) = info.proto
|
||||
&& let Some(proto_code) = proto.class().type_code(vm)
|
||||
&& let Some(proto_code) = proto.type_code(vm)
|
||||
{
|
||||
return proto_code == "u";
|
||||
return Ok(proto_code == "u");
|
||||
}
|
||||
}
|
||||
false
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
impl Constructor for PyCSimple {
|
||||
@@ -1121,15 +1163,27 @@ impl PyCSimple {
|
||||
return Ok(vm.ctx.none());
|
||||
}
|
||||
// Read null-terminated wide string at the address
|
||||
// Windows: wchar_t = u16 (UTF-16) -> use Wtf8Buf::from_wide for surrogate pairs
|
||||
// Unix: wchar_t = i32 (UTF-32) -> convert via char::from_u32
|
||||
unsafe {
|
||||
let w_ptr = ptr as *const libc::wchar_t;
|
||||
let len = libc::wcslen(w_ptr);
|
||||
let wchars = std::slice::from_raw_parts(w_ptr, len);
|
||||
let s: String = wchars
|
||||
.iter()
|
||||
.filter_map(|&c| char::from_u32(c as u32))
|
||||
.collect();
|
||||
return Ok(vm.ctx.new_str(s).into());
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
let wide: Vec<u16> = wchars.to_vec();
|
||||
let wtf8 = Wtf8Buf::from_wide(&wide);
|
||||
return Ok(vm.ctx.new_str(wtf8).into());
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let s: String = wchars
|
||||
.iter()
|
||||
.filter_map(|&c| char::from_u32(c as u32))
|
||||
.collect();
|
||||
return Ok(vm.ctx.new_str(s).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1349,12 +1403,25 @@ impl PyCSimple {
|
||||
|
||||
impl AsBuffer for PyCSimple {
|
||||
fn as_buffer(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyBuffer> {
|
||||
let buffer_len = zelf.0.buffer.read().len();
|
||||
let buf = PyBuffer::new(
|
||||
zelf.to_owned().into(),
|
||||
BufferDescriptor::simple(buffer_len, false), // readonly=false for ctypes
|
||||
&CDATA_BUFFER_METHODS,
|
||||
);
|
||||
let stg_info = zelf
|
||||
.class()
|
||||
.stg_info_opt()
|
||||
.expect("PyCSimple type must have StgInfo");
|
||||
let format = stg_info
|
||||
.format
|
||||
.clone()
|
||||
.map(Cow::Owned)
|
||||
.unwrap_or(Cow::Borrowed("B"));
|
||||
let itemsize = stg_info.size;
|
||||
// Simple types are scalars with ndim=0, shape=()
|
||||
let desc = BufferDescriptor {
|
||||
len: itemsize,
|
||||
readonly: false,
|
||||
itemsize,
|
||||
format,
|
||||
dim_desc: vec![],
|
||||
};
|
||||
let buf = PyBuffer::new(zelf.to_owned().into(), desc, &CDATA_BUFFER_METHODS);
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,12 +601,6 @@ impl PyCStructure {
|
||||
fn _b0_(&self) -> Option<PyObjectRef> {
|
||||
self.0.base.read().clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn _fields_(&self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
// Return the _fields_ from the class, not instance
|
||||
vm.ctx.none()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBuffer for PyCStructure {
|
||||
|
||||
Reference in new issue
Block a user