Skip to content

Commit 36b6d8f

Browse files
committed
fix sizeof/alignment
1 parent bea4eec commit 36b6d8f

File tree

10 files changed

+639
-212
lines changed

10 files changed

+639
-212
lines changed

.cspell.dict/cpython.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ stackdepth
5050
stringlib
5151
structseq
5252
subparams
53+
swappedbytes
5354
ticketer
5455
tok_oldval
5556
tvars

crates/vm/src/stdlib/ctypes.rs

Lines changed: 199 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ pub(crate) mod pointer;
99
pub(crate) mod structure;
1010
pub(crate) mod thunk;
1111
pub(crate) mod union;
12+
pub(crate) mod util;
1213

1314
use crate::builtins::PyModule;
1415
use crate::class::PyClassImpl;
1516
use crate::{Py, PyRef, VirtualMachine};
1617

17-
pub use crate::stdlib::ctypes::base::{CDataObject, PyCData, PyCSimple, PyCSimpleType};
18+
pub use crate::stdlib::ctypes::base::{PyCData, PyCSimple, PyCSimpleType};
1819

1920
pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py<PyModule>) {
2021
let ctx = &vm.ctx;
@@ -52,7 +53,7 @@ pub(crate) mod _ctypes {
5253
use crate::convert::ToPyObject;
5354
use crate::function::{Either, FuncArgs, OptionalArg};
5455
use crate::stdlib::ctypes::library;
55-
use crate::{AsObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine};
56+
use crate::{AsObject, PyObjectRef, PyPayload, PyResult, VirtualMachine};
5657
use crossbeam_utils::atomic::AtomicCell;
5758
use std::ffi::{
5859
c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong,
@@ -159,6 +160,11 @@ pub(crate) mod _ctypes {
159160
}
160161
}
161162

163+
/// Get alignment for a simple type - for C types, alignment equals size
164+
pub fn get_align(ty: &str) -> usize {
165+
get_size(ty)
166+
}
167+
162168
/// Get the size of a ctypes type from its type object
163169
#[allow(dead_code)]
164170
pub fn get_size_from_type(cls: &PyTypeRef, vm: &VirtualMachine) -> PyResult<usize> {
@@ -366,7 +372,10 @@ pub(crate) mod _ctypes {
366372
Ok(PyCSimple {
367373
_type_: tp_str,
368374
value: AtomicCell::new(vm.ctx.none()),
369-
cdata: rustpython_common::lock::PyRwLock::new(CDataObject::new(size)),
375+
cdata: rustpython_common::lock::PyRwLock::new(CDataObject::from_bytes(
376+
vec![0u8; size],
377+
None,
378+
)),
370379
})
371380
}
372381
} else {
@@ -378,98 +387,117 @@ pub(crate) mod _ctypes {
378387
}
379388

380389
/// Get the size of a ctypes type or instance
381-
///
382-
/// This function accepts:
383-
/// - A ctypes type (e.g., c_int, Structure subclass)
384-
/// - A ctypes instance (e.g., c_int(5))
385390
#[pyfunction(name = "sizeof")]
386391
pub fn size_of(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
387-
use super::array::PyCArray;
392+
use super::array::{PyCArray, PyCArrayType};
388393
use super::pointer::PyCPointer;
389-
use super::structure::PyCStructure;
390-
use super::union::PyCUnion;
391-
392-
// Check if obj is a type
393-
if let Ok(type_ref) = obj.clone().downcast::<crate::builtins::PyType>() {
394-
// It's a type - check what kind of ctypes type it is
395-
396-
// Simple types (c_int, c_char, etc.)
397-
if type_ref.fast_issubclass(PyCSimple::static_type()) {
398-
let zelf = new_simple_type(Either::B(&type_ref), vm)?;
399-
return Ok(get_size(zelf._type_.as_str()));
394+
use super::structure::{PyCStructType, PyCStructure};
395+
use super::union::{PyCUnion, PyCUnionType};
396+
397+
// 1. Instances with stg_info
398+
if obj.fast_isinstance(PyCArray::static_type()) {
399+
// Get stg_info from the type
400+
if let Some(type_obj) = obj.class().as_object().downcast_ref::<PyCArrayType>() {
401+
return Ok(type_obj.stg_info.size);
400402
}
401-
402-
// Array types
403-
if type_ref.fast_issubclass(PyCArray::static_type()) {
404-
// Get _length_ and element size
405-
if let Ok(length) = type_ref.as_object().get_attr("_length_", vm) {
406-
let length = usize::try_from_object(vm, length)?;
407-
if let Ok(elem_type) = type_ref.as_object().get_attr("_type_", vm) {
408-
let elem_size = size_of(elem_type, vm)?;
409-
return Ok(length * elem_size);
410-
}
411-
}
403+
}
404+
if let Some(structure) = obj.downcast_ref::<PyCStructure>() {
405+
return Ok(structure.cdata.read().size());
406+
}
407+
if obj.fast_isinstance(PyCUnion::static_type()) {
408+
// Get stg_info from the type
409+
if let Some(type_obj) = obj.class().as_object().downcast_ref::<PyCUnionType>() {
410+
return Ok(type_obj.stg_info.size);
412411
}
412+
}
413+
if let Some(simple) = obj.downcast_ref::<PyCSimple>() {
414+
return Ok(simple.cdata.read().size());
415+
}
416+
if obj.fast_isinstance(PyCPointer::static_type()) {
417+
return Ok(std::mem::size_of::<usize>());
418+
}
413419

414-
// Structure types - check for size_of_instances method
415-
if type_ref.fast_issubclass(PyCStructure::static_type())
416-
|| type_ref.fast_issubclass(PyCUnion::static_type())
420+
// 2. Types (metatypes with stg_info)
421+
if let Some(array_type) = obj.downcast_ref::<PyCArrayType>() {
422+
return Ok(array_type.stg_info.size);
423+
}
424+
425+
// 3. Type objects
426+
if let Ok(type_ref) = obj.clone().downcast::<crate::builtins::PyType>() {
427+
// Structure types - check if metaclass is or inherits from PyCStructType
428+
if type_ref
429+
.class()
430+
.fast_issubclass(PyCStructType::static_type())
417431
{
418-
if let Ok(size_method) = type_ref.as_object().get_attr("size_of_instances", vm) {
419-
let size = size_method.call(vec![], vm)?;
420-
return Ok(usize::try_from_object(vm, size)?);
421-
}
432+
return calculate_struct_size(&type_ref, vm);
433+
}
434+
// Union types - check if metaclass is or inherits from PyCUnionType
435+
if type_ref
436+
.class()
437+
.fast_issubclass(PyCUnionType::static_type())
438+
{
439+
return calculate_union_size(&type_ref, vm);
440+
}
441+
// Simple types (c_int, c_char, etc.)
442+
if type_ref.fast_issubclass(PyCSimple::static_type()) {
443+
let instance = new_simple_type(Either::B(&type_ref), vm)?;
444+
return Ok(get_size(&instance._type_));
422445
}
423-
424446
// Pointer types
425447
if type_ref.fast_issubclass(PyCPointer::static_type()) {
426448
return Ok(std::mem::size_of::<usize>());
427449
}
428-
429-
// Check for size_of_instances as a fallback
430-
if let Ok(size_method) = type_ref.as_object().get_attr("size_of_instances", vm) {
431-
let size = size_method.call(vec![], vm)?;
432-
return Ok(usize::try_from_object(vm, size)?);
433-
}
434-
435-
return Err(vm.new_type_error("this type has no size"));
436450
}
437451

438-
// It's an instance - get size from instance or its class
439-
440-
// Simple type instance
441-
if let Ok(simple) = obj.clone().downcast::<PyCSimple>() {
442-
return Ok(get_size(simple._type_.as_str()));
443-
}
444-
445-
// Array instance
446-
if let Ok(array) = obj.clone().downcast::<PyCArray>() {
447-
return Ok(array.cdata.read().size());
448-
}
449-
450-
// Structure instance
451-
if let Ok(structure) = obj.clone().downcast::<PyCStructure>() {
452-
return Ok(structure.cdata.read().size());
453-
}
454-
455-
// Union instance
456-
if let Ok(union) = obj.clone().downcast::<PyCUnion>() {
457-
return Ok(union.cdata.read().size());
458-
}
452+
Err(vm.new_type_error("this type has no size"))
453+
}
459454

460-
// Pointer instance
461-
if obj.fast_isinstance(PyCPointer::static_type()) {
462-
return Ok(std::mem::size_of::<usize>());
455+
/// Calculate Structure type size from _fields_ (sum of field sizes)
456+
fn calculate_struct_size(
457+
cls: &crate::builtins::PyTypeRef,
458+
vm: &VirtualMachine,
459+
) -> PyResult<usize> {
460+
use crate::AsObject;
461+
462+
if let Ok(fields_attr) = cls.as_object().get_attr("_fields_", vm) {
463+
let fields: Vec<PyObjectRef> = fields_attr.try_to_value(vm).unwrap_or_default();
464+
let mut total_size = 0usize;
465+
466+
for field in fields.iter() {
467+
if let Some(tuple) = field.downcast_ref::<crate::builtins::PyTuple>()
468+
&& let Some(field_type) = tuple.get(1)
469+
{
470+
// Recursively calculate field type size
471+
total_size += size_of(field_type.clone(), vm)?;
472+
}
473+
}
474+
return Ok(total_size);
463475
}
476+
Ok(0)
477+
}
464478

465-
// Check if the object has size_of_instances method (for custom types)
466-
if obj.has_attr("size_of_instances", vm)? {
467-
let size_method = obj.get_attr("size_of_instances", vm)?;
468-
let size = size_method.call(vec![], vm)?;
469-
return Ok(usize::try_from_object(vm, size)?);
479+
/// Calculate Union type size from _fields_ (max field size)
480+
fn calculate_union_size(
481+
cls: &crate::builtins::PyTypeRef,
482+
vm: &VirtualMachine,
483+
) -> PyResult<usize> {
484+
use crate::AsObject;
485+
486+
if let Ok(fields_attr) = cls.as_object().get_attr("_fields_", vm) {
487+
let fields: Vec<PyObjectRef> = fields_attr.try_to_value(vm).unwrap_or_default();
488+
let mut max_size = 0usize;
489+
490+
for field in fields.iter() {
491+
if let Some(tuple) = field.downcast_ref::<crate::builtins::PyTuple>()
492+
&& let Some(field_type) = tuple.get(1)
493+
{
494+
let field_size = size_of(field_type.clone(), vm)?;
495+
max_size = max_size.max(field_size);
496+
}
497+
}
498+
return Ok(max_size);
470499
}
471-
472-
Err(vm.new_type_error("this type has no size"))
500+
Ok(0)
473501
}
474502

475503
#[cfg(windows)]
@@ -630,9 +658,100 @@ pub(crate) mod _ctypes {
630658
}
631659

632660
#[pyfunction]
633-
fn alignment(_args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
634-
// TODO: RUSTPYTHON
635-
Err(vm.new_value_error("not implemented"))
661+
fn alignment(tp: Either<PyTypeRef, PyObjectRef>, vm: &VirtualMachine) -> PyResult<usize> {
662+
use super::array::{PyCArray, PyCArrayType};
663+
use super::base::PyCSimpleType;
664+
use super::pointer::PyCPointer;
665+
use super::structure::PyCStructure;
666+
use super::union::PyCUnion;
667+
668+
let obj = match &tp {
669+
Either::A(t) => t.as_object(),
670+
Either::B(o) => o.as_ref(),
671+
};
672+
673+
// Try to get alignment from stg_info directly (for instances)
674+
if let Some(array_type) = obj.downcast_ref::<PyCArrayType>() {
675+
return Ok(array_type.stg_info.align);
676+
}
677+
if obj.fast_isinstance(PyCSimple::static_type()) {
678+
// Get stg_info from the type by reading _type_ attribute
679+
let cls = obj.class().to_owned();
680+
let stg_info = PyCSimpleType::get_stg_info(&cls, vm);
681+
return Ok(stg_info.align);
682+
}
683+
if obj.fast_isinstance(PyCArray::static_type()) {
684+
// Get stg_info from the type
685+
if let Some(type_obj) = obj.class().as_object().downcast_ref::<PyCArrayType>() {
686+
return Ok(type_obj.stg_info.align);
687+
}
688+
}
689+
if obj.fast_isinstance(PyCStructure::static_type()) {
690+
// Calculate alignment from _fields_
691+
let cls = obj.class();
692+
return alignment(Either::A(cls.to_owned()), vm);
693+
}
694+
if obj.fast_isinstance(PyCPointer::static_type()) {
695+
// Pointer alignment is always pointer size
696+
return Ok(std::mem::align_of::<usize>());
697+
}
698+
if obj.fast_isinstance(PyCUnion::static_type()) {
699+
// Calculate alignment from _fields_
700+
let cls = obj.class();
701+
return alignment(Either::A(cls.to_owned()), vm);
702+
}
703+
704+
// Get the type object to check
705+
let type_obj: PyObjectRef = match &tp {
706+
Either::A(t) => t.clone().into(),
707+
Either::B(obj) => obj.class().to_owned().into(),
708+
};
709+
710+
// For type objects, try to get alignment from _type_ attribute
711+
if let Ok(type_attr) = type_obj.get_attr("_type_", vm) {
712+
// Array/Pointer: _type_ is the element type (a PyType)
713+
if let Ok(elem_type) = type_attr.clone().downcast::<crate::builtins::PyType>() {
714+
return alignment(Either::A(elem_type), vm);
715+
}
716+
// Simple type: _type_ is a single character string
717+
if let Ok(s) = type_attr.str(vm) {
718+
let ty = s.to_string();
719+
if ty.len() == 1 && SIMPLE_TYPE_CHARS.contains(ty.as_str()) {
720+
return Ok(get_align(&ty));
721+
}
722+
}
723+
}
724+
725+
// Structure/Union: max alignment of fields
726+
if let Ok(fields_attr) = type_obj.get_attr("_fields_", vm)
727+
&& let Ok(fields) = fields_attr.try_to_value::<Vec<PyObjectRef>>(vm)
728+
{
729+
let mut max_align = 1usize;
730+
for field in fields.iter() {
731+
if let Some(tuple) = field.downcast_ref::<crate::builtins::PyTuple>()
732+
&& let Some(field_type) = tuple.get(1)
733+
{
734+
let align =
735+
if let Ok(ft) = field_type.clone().downcast::<crate::builtins::PyType>() {
736+
alignment(Either::A(ft), vm).unwrap_or(1)
737+
} else {
738+
1
739+
};
740+
max_align = max_align.max(align);
741+
}
742+
}
743+
return Ok(max_align);
744+
}
745+
746+
// For instances, delegate to their class
747+
if let Either::B(obj) = &tp
748+
&& !obj.class().is(vm.ctx.types.type_type.as_ref())
749+
{
750+
return alignment(Either::A(obj.class().to_owned()), vm);
751+
}
752+
753+
// No alignment info found
754+
Err(vm.new_type_error("no alignment info"))
636755
}
637756

638757
#[pyfunction]

0 commit comments

Comments
 (0)