@@ -9,12 +9,13 @@ pub(crate) mod pointer;
99pub ( crate ) mod structure;
1010pub ( crate ) mod thunk;
1111pub ( crate ) mod union;
12+ pub ( crate ) mod util;
1213
1314use crate :: builtins:: PyModule ;
1415use crate :: class:: PyClassImpl ;
1516use 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
1920pub 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