|
Expand Up
|
@@ -17,10 +17,9 @@ use alloc::borrow::Cow; |
|
|
use num_traits::{Signed, ToPrimitive}; |
|
|
|
|
|
use rustpython_host_env::ctypes::{ |
|
|
ArrayElementWriteValue, DecodedValue, WCHAR_SIZE, WCharArrayWriteError, char_array_field_value, |
|
|
int_to_sized_bytes, read_array_element, simple_type_size, uint_to_sized_bytes, |
|
|
wchar_from_bytes, write_array_element, write_char_array_raw, write_char_array_value, |
|
|
write_wchar_array_value, wstring_from_bytes, zeroed_bytes, |
|
|
DecodedValue, WCHAR_SIZE, WCharArrayWriteError, char_array_field_value, read_array_element, |
|
|
simple_type_size, wchar_from_bytes, write_char_array_raw, write_char_array_value, |
|
|
write_wchar_array_value, wstring_from_bytes, |
|
|
}; |
|
|
|
|
|
fn cow_bytes_as_mut<'buf>(buffer: &'buf mut Cow<'_, [u8]>) -> &'buf mut [u8] { |
|
Expand Down
Expand Up
|
@@ -508,33 +507,6 @@ impl PyCArray { |
|
|
PyGenericAlias::from_args(cls, object, 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) |
|
|
match size { |
|
|
1 => { |
|
|
if let Some(v) = i.to_u8() { |
|
|
vec![v] |
|
|
} else { |
|
|
vec![i.to_i8().unwrap_or(0) as u8] |
|
|
} |
|
|
} |
|
|
2 => i.to_u16().map_or_else( |
|
|
|| int_to_sized_bytes(i.to_i16().unwrap_or(0).into(), 2), |
|
|
|v| uint_to_sized_bytes(v.into(), 2), |
|
|
), |
|
|
4 => i.to_u32().map_or_else( |
|
|
|| int_to_sized_bytes(i.to_i32().unwrap_or(0).into(), 4), |
|
|
|v| uint_to_sized_bytes(v.into(), 4), |
|
|
), |
|
|
8 => i.to_u64().map_or_else( |
|
|
|| int_to_sized_bytes(i.to_i64().unwrap_or(0), 8), |
|
|
|v| uint_to_sized_bytes(v, 8), |
|
|
), |
|
|
_ => zeroed_bytes(size), |
|
|
} |
|
|
} |
|
|
|
|
|
fn getitem_by_index(zelf: &Py<Self>, i: isize, vm: &VirtualMachine) -> PyResult { |
|
|
let stg = zelf.class().stg_info_opt(); |
|
|
let length = stg.as_ref().map_or(0, |i| i.length) as isize; |
|
Expand Down
Expand Up
|
@@ -616,231 +588,39 @@ impl PyCArray { |
|
|
} |
|
|
} |
|
|
|
|
|
/// Helper to write an element value to a buffer at given offset |
|
|
fn write_element_to_buffer( |
|
|
offset: usize, |
|
|
element_size: usize, |
|
|
type_code: Option<&str>, |
|
|
value: &PyObject, |
|
|
zelf: &Py<Self>, |
|
|
index: usize, |
|
|
vm: &VirtualMachine, |
|
|
) -> PyResult<()> { |
|
|
// Conversions and reference replacement may call Python. Only the raw |
|
|
// storage write runs under the buffer lock. |
|
|
let write = |value| { |
|
|
let mut buffer = zelf.0.buffer.write(); |
|
|
write_array_element(cow_bytes_as_mut(&mut buffer), offset, value); |
|
|
}; |
|
|
match type_code { |
|
|
Some("c") => { |
|
|
if let Some(b) = value.downcast_ref::<PyBytes>() { |
|
|
write(ArrayElementWriteValue::Byte( |
|
|
b.as_bytes().first().copied().unwrap_or(0), |
|
|
)); |
|
|
} else if let Ok(int_val) = value.try_int(vm) { |
|
|
write(ArrayElementWriteValue::Byte( |
|
|
int_val.as_bigint().to_u8().unwrap_or(0), |
|
|
)); |
|
|
} else { |
|
|
return Err(vm.new_type_error("an integer or bytes of length 1 is required")); |
|
|
} |
|
|
} |
|
|
Some("u") => { |
|
|
if let Some(s) = value.downcast_ref::<PyStr>() { |
|
|
let code = s.as_wtf8().code_points().next().map_or(0, |c| c.to_u32()); |
|
|
write(ArrayElementWriteValue::Wchar(code)); |
|
|
} else { |
|
|
return Err(vm.new_type_error("unicode string expected")); |
|
|
} |
|
|
} |
|
|
Some("z") => { |
|
|
let mut hidden = None; |
|
|
let (ptr_val, converted) = if value.is(&vm.ctx.none) { |
|
|
(0usize, None) |
|
|
} else if let Some(bytes) = value.downcast_ref::<PyBytes>() { |
|
|
let (kept_alive, ptr) = super::base::ensure_z_null_terminated(bytes, vm); |
|
|
hidden = Some(kept_alive); |
|
|
(ptr, Some(value.to_owned())) |
|
|
} else if let Some(simple) = value.downcast_ref::<super::PyCSimple>() |
|
|
&& value.class().type_code(vm).as_deref() == Some("z") |
|
|
{ |
|
|
let buffer = simple.0.buffer.read(); |
|
|
( |
|
|
rustpython_host_env::ctypes::read_pointer_from_buffer(&buffer), |
|
|
None, |
|
|
) |
|
|
} else if let Ok(int_val) = value.try_index(vm) { |
|
|
(int_val.as_bigint().to_usize().unwrap_or(0), None) |
|
|
} else { |
|
|
return Err(vm.new_type_error(format!( |
|
|
"bytes or integer address expected instead of {} instance", |
|
|
value.class().name() |
|
|
))); |
|
|
}; |
|
|
write(ArrayElementWriteValue::Pointer { |
|
|
value: ptr_val, |
|
|
size: element_size, |
|
|
}); |
|
|
if let Some(c) = converted { |
|
|
return zelf.0.keep_ref_with_hidden(index, c, hidden, vm); |
|
|
} |
|
|
} |
|
|
Some("Z") => { |
|
|
let (ptr_val, converted) = if value.is(&vm.ctx.none) { |
|
|
(0usize, None) |
|
|
} else if let Some(s) = value.downcast_ref::<PyStr>() { |
|
|
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_wtf8(), vm); |
|
|
(ptr, Some(holder)) |
|
|
} else if let Some(simple) = value.downcast_ref::<super::PyCSimple>() |
|
|
&& value.class().type_code(vm).as_deref() == Some("Z") |
|
|
{ |
|
|
let buffer = simple.0.buffer.read(); |
|
|
( |
|
|
rustpython_host_env::ctypes::read_pointer_from_buffer(&buffer), |
|
|
None, |
|
|
) |
|
|
} else if let Ok(int_val) = value.try_index(vm) { |
|
|
(int_val.as_bigint().to_usize().unwrap_or(0), None) |
|
|
} else { |
|
|
return Err(vm.new_type_error("unicode string or integer address expected")); |
|
|
}; |
|
|
write(ArrayElementWriteValue::Pointer { |
|
|
value: ptr_val, |
|
|
size: element_size, |
|
|
}); |
|
|
if let Some(c) = converted { |
|
|
return zelf.0.keep_ref(index, c, vm); |
|
|
} |
|
|
} |
|
|
Some("O") => { |
|
|
// A py_object instance contributes its stored reference, while |
|
|
// other Python objects are the value itself. |
|
|
if value.downcast_ref::<PyCData>().is_none() { |
|
|
write(ArrayElementWriteValue::Pointer { |
|
|
value: value.get_id(), |
|
|
size: element_size, |
|
|
}); |
|
|
return zelf.0.keep_ref(index, value.to_owned(), vm); |
|
|
} |
|
|
let stg = zelf.class().stg_info(vm)?; |
|
|
let element_type = stg.element_type.as_ref().unwrap(); |
|
|
if !value.fast_isinstance(element_type) { |
|
|
return Err(vm.new_type_error(format!( |
|
|
"expected {} instance, not {}", |
|
|
element_type.name(), |
|
|
value.class().name() |
|
|
))); |
|
|
} |
|
|
let cdata = value.downcast_ref::<PyCData>().unwrap(); |
|
|
let bytes = cdata.buffer.read().to_vec(); |
|
|
write(ArrayElementWriteValue::Bytes { |
|
|
bytes: &bytes, |
|
|
size: element_size, |
|
|
}); |
|
|
} |
|
|
Some("f") => { |
|
|
// c_float: convert int/float to f32 bytes |
|
|
let f32_val = if let Ok(float_val) = value.try_float(vm) { |
|
|
float_val.to_f64() as f32 |
|
|
} else if let Ok(int_val) = value.try_int(vm) { |
|
|
int_val.as_bigint().to_f64().unwrap_or(0.0) as f32 |
|
|
} else { |
|
|
return Err(vm.new_type_error("a float is required")); |
|
|
}; |
|
|
write(ArrayElementWriteValue::Float { |
|
|
value: f32_val.into(), |
|
|
size: 4, |
|
|
}); |
|
|
} |
|
|
Some("d" | "g") => { |
|
|
// c_double / c_longdouble: convert int/float to f64 bytes |
|
|
let f64_val = if let Ok(float_val) = value.try_float(vm) { |
|
|
float_val.to_f64() |
|
|
} else if let Ok(int_val) = value.try_int(vm) { |
|
|
int_val.as_bigint().to_f64().unwrap_or(0.0) |
|
|
} else { |
|
|
return Err(vm.new_type_error("a float is required")); |
|
|
}; |
|
|
write(ArrayElementWriteValue::Float { |
|
|
value: f64_val, |
|
|
size: 8, |
|
|
}); |
|
|
// For "g" type, remaining bytes stay zero |
|
|
} |
|
|
_ => { |
|
|
// Handle ctypes instances (copy their buffer) |
|
|
if let Some(cdata) = value.downcast_ref::<PyCData>() { |
|
|
let src_buffer = cdata.buffer.read().to_vec(); |
|
|
write(ArrayElementWriteValue::Bytes { |
|
|
bytes: &src_buffer, |
|
|
size: element_size, |
|
|
}); |
|
|
// Other types: use int_to_bytes |
|
|
} else if let Ok(int_val) = value.try_int(vm) { |
|
|
let bytes = Self::int_to_bytes(int_val.as_bigint(), element_size); |
|
|
write(ArrayElementWriteValue::Bytes { |
|
|
bytes: &bytes, |
|
|
size: element_size, |
|
|
}); |
|
|
} else { |
|
|
return Err(vm.new_type_error(format!( |
|
|
"expected {} instance, not {}", |
|
|
type_code.unwrap_or("value"), |
|
|
value.class().name() |
|
|
))); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
// KeepRef |
|
|
if super::base::PyCData::should_keep_ref(value) { |
|
|
zelf.0.keep_cdata(index, value, vm)?; |
|
|
} |
|
|
|
|
|
Ok(()) |
|
|
} |
|
|
|
|
|
fn setitem_by_index( |
|
|
zelf: &Py<Self>, |
|
|
i: isize, |
|
|
value: &PyObject, |
|
|
vm: &VirtualMachine, |
|
|
) -> PyResult<()> { |
|
|
let stg = zelf.class().stg_info_opt(); |
|
|
let length = stg.as_ref().map_or(0, |i| i.length) as isize; |
|
|
let (length, element_size, element_type) = { |
|
|
let stg = zelf.class().stg_info(vm)?; |
|
|
( |
|
|
stg.length as isize, |
|
|
stg.element_size, |
|
|
stg.element_type |
|
|
.clone() |
|
|
.ok_or_else(|| vm.new_type_error("no element type"))?, |
|
|
) |
|
|
}; |
|
|
let index = if i < 0 { length + i } else { i }; |
|
|
if index < 0 || index >= length { |
|
|
return Err(vm.new_index_error("invalid index")); |
|
|
} |
|
|
let index = index as usize; |
|
|
let element_size = stg.as_ref().map_or(0, |i| i.element_size); |
|
|
let offset = index * element_size; |
|
|
let type_code = Self::get_element_type_code(zelf, vm); |
|
|
|
|
|
if let Some(element_type) = stg.as_ref().and_then(|info| info.element_type.as_ref()) |
|
|
&& super::simple::is_swapped(element_type) |
|
|
&& value.downcast_ref::<PyCData>().is_none() |
|
|
{ |
|
|
let type_code = type_code |
|
|
.as_deref() |
|
|
.ok_or_else(|| vm.new_type_error("no _type_ attribute"))?; |
|
|
let bytes = super::simple::swapped_value_bytes(type_code, value, vm)?; |
|
|
zelf.0.write_bytes_at_offset(offset, &bytes); |
|
|
return Ok(()); |
|
|
} |
|
|
|
|
|
// A view's buffer already points to its element. Its base can be a |
|
|
// pointer object, whose own buffer stores an address rather than data. |
|
|
Self::write_element_to_buffer( |
|
|
offset, |
|
|
element_size, |
|
|
type_code.as_deref(), |
|
|
let prepared = super::base::CDataValue::new( |
|
|
&element_type, |
|
|
value, |
|
|
zelf, |
|
|
index, |
|
|
element_size, |
|
|
super::simple::is_swapped(&element_type), |
|
|
vm, |
|
|
) |
|
|
)?; |
|
|
// A view's buffer already points to its element. Its base can be a |
|
|
// pointer object, whose own buffer stores an address rather than data. |
|
|
zelf.0 |
|
|
.write_bytes_at_offset(index * element_size, &prepared.bytes); |
|
|
prepared.keep(&zelf.0, index, vm) |
|
|
} |
|
|
|
|
|
// Array_subscript |
|
Expand Down
|
|