Skip to content

Commit eb6ff46

Browse files
committed
Fix ctypes assignment conversion and backing-buffer retention
Assisted-by: Codex:GPT-6
1 parent ebb6a78 commit eb6ff46

5 files changed

Lines changed: 520 additions & 783 deletions

File tree

‎crates/vm/src/stdlib/_ctypes/array.rs‎

Lines changed: 23 additions & 243 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ use alloc::borrow::Cow;
1717
use num_traits::{Signed, ToPrimitive};
1818

1919
use rustpython_host_env::ctypes::{
20-
ArrayElementWriteValue, DecodedValue, WCHAR_SIZE, WCharArrayWriteError, char_array_field_value,
21-
int_to_sized_bytes, read_array_element, simple_type_size, uint_to_sized_bytes,
22-
wchar_from_bytes, write_array_element, write_char_array_raw, write_char_array_value,
23-
write_wchar_array_value, wstring_from_bytes, zeroed_bytes,
20+
DecodedValue, WCHAR_SIZE, WCharArrayWriteError, char_array_field_value, read_array_element,
21+
simple_type_size, wchar_from_bytes, write_char_array_raw, write_char_array_value,
22+
write_wchar_array_value, wstring_from_bytes,
2423
};
2524

2625
fn cow_bytes_as_mut<'buf>(buffer: &'buf mut Cow<'_, [u8]>) -> &'buf mut [u8] {
@@ -508,33 +507,6 @@ impl PyCArray {
508507
PyGenericAlias::from_args(cls, object, vm)
509508
}
510509

511-
fn int_to_bytes(i: &malachite_bigint::BigInt, size: usize) -> Vec<u8> {
512-
// Try unsigned first (handles values like 0xFFFFFFFF that overflow signed)
513-
// then fall back to signed (handles negative values)
514-
match size {
515-
1 => {
516-
if let Some(v) = i.to_u8() {
517-
vec![v]
518-
} else {
519-
vec![i.to_i8().unwrap_or(0) as u8]
520-
}
521-
}
522-
2 => i.to_u16().map_or_else(
523-
|| int_to_sized_bytes(i.to_i16().unwrap_or(0).into(), 2),
524-
|v| uint_to_sized_bytes(v.into(), 2),
525-
),
526-
4 => i.to_u32().map_or_else(
527-
|| int_to_sized_bytes(i.to_i32().unwrap_or(0).into(), 4),
528-
|v| uint_to_sized_bytes(v.into(), 4),
529-
),
530-
8 => i.to_u64().map_or_else(
531-
|| int_to_sized_bytes(i.to_i64().unwrap_or(0), 8),
532-
|v| uint_to_sized_bytes(v, 8),
533-
),
534-
_ => zeroed_bytes(size),
535-
}
536-
}
537-
538510
fn getitem_by_index(zelf: &Py<Self>, i: isize, vm: &VirtualMachine) -> PyResult {
539511
let stg = zelf.class().stg_info_opt();
540512
let length = stg.as_ref().map_or(0, |i| i.length) as isize;
@@ -616,231 +588,39 @@ impl PyCArray {
616588
}
617589
}
618590

619-
/// Helper to write an element value to a buffer at given offset
620-
fn write_element_to_buffer(
621-
offset: usize,
622-
element_size: usize,
623-
type_code: Option<&str>,
624-
value: &PyObject,
625-
zelf: &Py<Self>,
626-
index: usize,
627-
vm: &VirtualMachine,
628-
) -> PyResult<()> {
629-
// Conversions and reference replacement may call Python. Only the raw
630-
// storage write runs under the buffer lock.
631-
let write = |value| {
632-
let mut buffer = zelf.0.buffer.write();
633-
write_array_element(cow_bytes_as_mut(&mut buffer), offset, value);
634-
};
635-
match type_code {
636-
Some("c") => {
637-
if let Some(b) = value.downcast_ref::<PyBytes>() {
638-
write(ArrayElementWriteValue::Byte(
639-
b.as_bytes().first().copied().unwrap_or(0),
640-
));
641-
} else if let Ok(int_val) = value.try_int(vm) {
642-
write(ArrayElementWriteValue::Byte(
643-
int_val.as_bigint().to_u8().unwrap_or(0),
644-
));
645-
} else {
646-
return Err(vm.new_type_error("an integer or bytes of length 1 is required"));
647-
}
648-
}
649-
Some("u") => {
650-
if let Some(s) = value.downcast_ref::<PyStr>() {
651-
let code = s.as_wtf8().code_points().next().map_or(0, |c| c.to_u32());
652-
write(ArrayElementWriteValue::Wchar(code));
653-
} else {
654-
return Err(vm.new_type_error("unicode string expected"));
655-
}
656-
}
657-
Some("z") => {
658-
let mut hidden = None;
659-
let (ptr_val, converted) = if value.is(&vm.ctx.none) {
660-
(0usize, None)
661-
} else if let Some(bytes) = value.downcast_ref::<PyBytes>() {
662-
let (kept_alive, ptr) = super::base::ensure_z_null_terminated(bytes, vm);
663-
hidden = Some(kept_alive);
664-
(ptr, Some(value.to_owned()))
665-
} else if let Some(simple) = value.downcast_ref::<super::PyCSimple>()
666-
&& value.class().type_code(vm).as_deref() == Some("z")
667-
{
668-
let buffer = simple.0.buffer.read();
669-
(
670-
rustpython_host_env::ctypes::read_pointer_from_buffer(&buffer),
671-
None,
672-
)
673-
} else if let Ok(int_val) = value.try_index(vm) {
674-
(int_val.as_bigint().to_usize().unwrap_or(0), None)
675-
} else {
676-
return Err(vm.new_type_error(format!(
677-
"bytes or integer address expected instead of {} instance",
678-
value.class().name()
679-
)));
680-
};
681-
write(ArrayElementWriteValue::Pointer {
682-
value: ptr_val,
683-
size: element_size,
684-
});
685-
if let Some(c) = converted {
686-
return zelf.0.keep_ref_with_hidden(index, c, hidden, vm);
687-
}
688-
}
689-
Some("Z") => {
690-
let (ptr_val, converted) = if value.is(&vm.ctx.none) {
691-
(0usize, None)
692-
} else if let Some(s) = value.downcast_ref::<PyStr>() {
693-
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_wtf8(), vm);
694-
(ptr, Some(holder))
695-
} else if let Some(simple) = value.downcast_ref::<super::PyCSimple>()
696-
&& value.class().type_code(vm).as_deref() == Some("Z")
697-
{
698-
let buffer = simple.0.buffer.read();
699-
(
700-
rustpython_host_env::ctypes::read_pointer_from_buffer(&buffer),
701-
None,
702-
)
703-
} else if let Ok(int_val) = value.try_index(vm) {
704-
(int_val.as_bigint().to_usize().unwrap_or(0), None)
705-
} else {
706-
return Err(vm.new_type_error("unicode string or integer address expected"));
707-
};
708-
write(ArrayElementWriteValue::Pointer {
709-
value: ptr_val,
710-
size: element_size,
711-
});
712-
if let Some(c) = converted {
713-
return zelf.0.keep_ref(index, c, vm);
714-
}
715-
}
716-
Some("O") => {
717-
// A py_object instance contributes its stored reference, while
718-
// other Python objects are the value itself.
719-
if value.downcast_ref::<PyCData>().is_none() {
720-
write(ArrayElementWriteValue::Pointer {
721-
value: value.get_id(),
722-
size: element_size,
723-
});
724-
return zelf.0.keep_ref(index, value.to_owned(), vm);
725-
}
726-
let stg = zelf.class().stg_info(vm)?;
727-
let element_type = stg.element_type.as_ref().unwrap();
728-
if !value.fast_isinstance(element_type) {
729-
return Err(vm.new_type_error(format!(
730-
"expected {} instance, not {}",
731-
element_type.name(),
732-
value.class().name()
733-
)));
734-
}
735-
let cdata = value.downcast_ref::<PyCData>().unwrap();
736-
let bytes = cdata.buffer.read().to_vec();
737-
write(ArrayElementWriteValue::Bytes {
738-
bytes: &bytes,
739-
size: element_size,
740-
});
741-
}
742-
Some("f") => {
743-
// c_float: convert int/float to f32 bytes
744-
let f32_val = if let Ok(float_val) = value.try_float(vm) {
745-
float_val.to_f64() as f32
746-
} else if let Ok(int_val) = value.try_int(vm) {
747-
int_val.as_bigint().to_f64().unwrap_or(0.0) as f32
748-
} else {
749-
return Err(vm.new_type_error("a float is required"));
750-
};
751-
write(ArrayElementWriteValue::Float {
752-
value: f32_val.into(),
753-
size: 4,
754-
});
755-
}
756-
Some("d" | "g") => {
757-
// c_double / c_longdouble: convert int/float to f64 bytes
758-
let f64_val = if let Ok(float_val) = value.try_float(vm) {
759-
float_val.to_f64()
760-
} else if let Ok(int_val) = value.try_int(vm) {
761-
int_val.as_bigint().to_f64().unwrap_or(0.0)
762-
} else {
763-
return Err(vm.new_type_error("a float is required"));
764-
};
765-
write(ArrayElementWriteValue::Float {
766-
value: f64_val,
767-
size: 8,
768-
});
769-
// For "g" type, remaining bytes stay zero
770-
}
771-
_ => {
772-
// Handle ctypes instances (copy their buffer)
773-
if let Some(cdata) = value.downcast_ref::<PyCData>() {
774-
let src_buffer = cdata.buffer.read().to_vec();
775-
write(ArrayElementWriteValue::Bytes {
776-
bytes: &src_buffer,
777-
size: element_size,
778-
});
779-
// Other types: use int_to_bytes
780-
} else if let Ok(int_val) = value.try_int(vm) {
781-
let bytes = Self::int_to_bytes(int_val.as_bigint(), element_size);
782-
write(ArrayElementWriteValue::Bytes {
783-
bytes: &bytes,
784-
size: element_size,
785-
});
786-
} else {
787-
return Err(vm.new_type_error(format!(
788-
"expected {} instance, not {}",
789-
type_code.unwrap_or("value"),
790-
value.class().name()
791-
)));
792-
}
793-
}
794-
}
795-
796-
// KeepRef
797-
if super::base::PyCData::should_keep_ref(value) {
798-
zelf.0.keep_cdata(index, value, vm)?;
799-
}
800-
801-
Ok(())
802-
}
803-
804591
fn setitem_by_index(
805592
zelf: &Py<Self>,
806593
i: isize,
807594
value: &PyObject,
808595
vm: &VirtualMachine,
809596
) -> PyResult<()> {
810-
let stg = zelf.class().stg_info_opt();
811-
let length = stg.as_ref().map_or(0, |i| i.length) as isize;
597+
let (length, element_size, element_type) = {
598+
let stg = zelf.class().stg_info(vm)?;
599+
(
600+
stg.length as isize,
601+
stg.element_size,
602+
stg.element_type
603+
.clone()
604+
.ok_or_else(|| vm.new_type_error("no element type"))?,
605+
)
606+
};
812607
let index = if i < 0 { length + i } else { i };
813608
if index < 0 || index >= length {
814609
return Err(vm.new_index_error("invalid index"));
815610
}
816611
let index = index as usize;
817-
let element_size = stg.as_ref().map_or(0, |i| i.element_size);
818-
let offset = index * element_size;
819-
let type_code = Self::get_element_type_code(zelf, vm);
820-
821-
if let Some(element_type) = stg.as_ref().and_then(|info| info.element_type.as_ref())
822-
&& super::simple::is_swapped(element_type)
823-
&& value.downcast_ref::<PyCData>().is_none()
824-
{
825-
let type_code = type_code
826-
.as_deref()
827-
.ok_or_else(|| vm.new_type_error("no _type_ attribute"))?;
828-
let bytes = super::simple::swapped_value_bytes(type_code, value, vm)?;
829-
zelf.0.write_bytes_at_offset(offset, &bytes);
830-
return Ok(());
831-
}
832-
833-
// A view's buffer already points to its element. Its base can be a
834-
// pointer object, whose own buffer stores an address rather than data.
835-
Self::write_element_to_buffer(
836-
offset,
837-
element_size,
838-
type_code.as_deref(),
612+
let prepared = super::base::CDataValue::new(
613+
&element_type,
839614
value,
840-
zelf,
841-
index,
615+
element_size,
616+
super::simple::is_swapped(&element_type),
842617
vm,
843-
)
618+
)?;
619+
// A view's buffer already points to its element. Its base can be a
620+
// pointer object, whose own buffer stores an address rather than data.
621+
zelf.0
622+
.write_bytes_at_offset(index * element_size, &prepared.bytes);
623+
prepared.keep(&zelf.0, index, vm)
844624
}
845625

846626
// Array_subscript

0 commit comments

Comments
 (0)