@@ -360,6 +360,49 @@ pub(super) static CDATA_BUFFER_METHODS: BufferMethods = BufferMethods {
360360 retain : |_| { } ,
361361} ;
362362
363+ /// Convert Vec<T> to Vec<u8> by reinterpreting the memory (same allocation).
364+ fn vec_to_bytes < T > ( vec : Vec < T > ) -> Vec < u8 > {
365+ let len = vec. len ( ) * std:: mem:: size_of :: < T > ( ) ;
366+ let cap = vec. capacity ( ) * std:: mem:: size_of :: < T > ( ) ;
367+ let ptr = vec. as_ptr ( ) as * mut u8 ;
368+ std:: mem:: forget ( vec) ;
369+ unsafe { Vec :: from_raw_parts ( ptr, len, cap) }
370+ }
371+
372+ /// Ensure PyBytes is null-terminated. Returns (PyBytes to keep, pointer).
373+ /// If already contains null, returns original. Otherwise creates new with null appended.
374+ pub ( super ) fn ensure_z_null_terminated (
375+ bytes : & PyBytes ,
376+ vm : & VirtualMachine ,
377+ ) -> ( PyObjectRef , usize ) {
378+ let data = bytes. as_bytes ( ) ;
379+ if data. contains ( & 0 ) {
380+ // Already has null, use original
381+ let original: PyObjectRef = vm. ctx . new_bytes ( data. to_vec ( ) ) . into ( ) ;
382+ ( original, data. as_ptr ( ) as usize )
383+ } else {
384+ // Create new with null appended
385+ let mut buffer = data. to_vec ( ) ;
386+ buffer. push ( 0 ) ;
387+ let ptr = buffer. as_ptr ( ) as usize ;
388+ let new_bytes: PyObjectRef = vm. ctx . new_bytes ( buffer) . into ( ) ;
389+ ( new_bytes, ptr)
390+ }
391+ }
392+
393+ /// Convert str to null-terminated wchar_t buffer. Returns (PyBytes holder, pointer).
394+ pub ( super ) fn str_to_wchar_bytes ( s : & str , vm : & VirtualMachine ) -> ( PyObjectRef , usize ) {
395+ let wchars: Vec < libc:: wchar_t > = s
396+ . chars ( )
397+ . map ( |c| c as libc:: wchar_t )
398+ . chain ( std:: iter:: once ( 0 ) )
399+ . collect ( ) ;
400+ let ptr = wchars. as_ptr ( ) as usize ;
401+ let bytes = vec_to_bytes ( wchars) ;
402+ let holder: PyObjectRef = vm. ctx . new_bytes ( bytes) . into ( ) ;
403+ ( holder, ptr)
404+ }
405+
363406/// PyCData - base type for all ctypes data types
364407#[ pyclass( name = "_CData" , module = "_ctypes" ) ]
365408#[ derive( Debug , PyPayload ) ]
@@ -894,10 +937,10 @@ impl PyCData {
894937 . ok ( )
895938 . and_then ( |attr| attr. downcast_ref :: < PyStr > ( ) . map ( |s| s. to_string ( ) ) ) ;
896939
897- let mut bytes = if let Some ( type_code) = & field_type_code {
940+ let ( mut bytes, converted_value ) = if let Some ( type_code) = & field_type_code {
898941 PyCField :: value_to_bytes_for_type ( type_code, & value, size, vm) ?
899942 } else {
900- PyCField :: value_to_bytes ( & value, size, vm) ?
943+ ( PyCField :: value_to_bytes ( & value, size, vm) ?, None )
901944 } ;
902945
903946 // Swap bytes for opposite endianness
@@ -907,9 +950,9 @@ impl PyCData {
907950
908951 self . write_bytes_at_offset ( offset, & bytes) ;
909952
910- // KeepRef
911- if value . downcast_ref :: < PyBytes > ( ) . is_some ( ) {
912- self . keep_ref ( index, value , vm) ?;
953+ // KeepRef: for z/Z types use converted value, otherwise use original
954+ if let Some ( converted ) = converted_value {
955+ self . keep_ref ( index, converted , vm) ?;
913956 } else if Self :: should_keep_ref ( & value) {
914957 let to_keep = Self :: get_kept_objects ( & value, vm) ;
915958 self . keep_ref ( index, to_keep, vm) ?;
@@ -1360,13 +1403,14 @@ impl PyCField {
13601403 }
13611404 }
13621405
1363- /// Convert a Python value to bytes with type-specific handling for pointer types
1406+ /// Convert a Python value to bytes with type-specific handling for pointer types.
1407+ /// Returns (bytes, optional holder for wchar buffer).
13641408 fn value_to_bytes_for_type (
13651409 type_code : & str ,
13661410 value : & PyObject ,
13671411 size : usize ,
13681412 vm : & VirtualMachine ,
1369- ) -> PyResult < Vec < u8 > > {
1413+ ) -> PyResult < ( Vec < u8 > , Option < PyObjectRef > ) > {
13701414 match type_code {
13711415 // c_float: always convert to float first (f_set)
13721416 "f" => {
@@ -1381,7 +1425,7 @@ impl PyCField {
13811425 ) ) ) ;
13821426 } ;
13831427 let val = f as f32 ;
1384- Ok ( val. to_ne_bytes ( ) . to_vec ( ) )
1428+ Ok ( ( val. to_ne_bytes ( ) . to_vec ( ) , None ) )
13851429 }
13861430 // c_double: always convert to float first (d_set)
13871431 "d" => {
@@ -1395,7 +1439,7 @@ impl PyCField {
13951439 value. class( ) . name( )
13961440 ) ) ) ;
13971441 } ;
1398- Ok ( f. to_ne_bytes ( ) . to_vec ( ) )
1442+ Ok ( ( f. to_ne_bytes ( ) . to_vec ( ) , None ) )
13991443 }
14001444 // c_longdouble: convert to float (treated as f64 in RustPython)
14011445 "g" => {
@@ -1409,18 +1453,17 @@ impl PyCField {
14091453 value. class( ) . name( )
14101454 ) ) ) ;
14111455 } ;
1412- Ok ( f. to_ne_bytes ( ) . to_vec ( ) )
1456+ Ok ( ( f. to_ne_bytes ( ) . to_vec ( ) , None ) )
14131457 }
14141458 "z" => {
1415- // c_char_p: store pointer to bytes data
1459+ // c_char_p: store pointer to null-terminated bytes
14161460 if let Some ( bytes) = value. downcast_ref :: < PyBytes > ( ) {
1417- let addr = bytes . as_bytes ( ) . as_ptr ( ) as usize ;
1461+ let ( converted , ptr ) = ensure_z_null_terminated ( bytes , vm ) ;
14181462 let mut result = vec ! [ 0u8 ; size] ;
1419- let addr_bytes = addr . to_ne_bytes ( ) ;
1463+ let addr_bytes = ptr . to_ne_bytes ( ) ;
14201464 let len = std:: cmp:: min ( addr_bytes. len ( ) , size) ;
14211465 result[ ..len] . copy_from_slice ( & addr_bytes[ ..len] ) ;
1422- result. push ( 0 ) ;
1423- return Ok ( result) ;
1466+ return Ok ( ( result, Some ( converted) ) ) ;
14241467 }
14251468 // Integer address
14261469 if let Ok ( int_val) = value. try_index ( vm) {
@@ -1429,32 +1472,23 @@ impl PyCField {
14291472 let bytes = v. to_ne_bytes ( ) ;
14301473 let len = std:: cmp:: min ( bytes. len ( ) , size) ;
14311474 result[ ..len] . copy_from_slice ( & bytes[ ..len] ) ;
1432- return Ok ( result) ;
1475+ return Ok ( ( result, None ) ) ;
14331476 }
14341477 // None -> NULL pointer
14351478 if vm. is_none ( value) {
1436- return Ok ( vec ! [ 0u8 ; size] ) ;
1479+ return Ok ( ( vec ! [ 0u8 ; size] , None ) ) ;
14371480 }
1438- PyCField :: value_to_bytes ( value, size, vm)
1481+ Ok ( ( PyCField :: value_to_bytes ( value, size, vm) ? , None ) )
14391482 }
14401483 "Z" => {
1441- // c_wchar_p: store pointer to wide string
1484+ // c_wchar_p: store pointer to null-terminated wchar_t buffer
14421485 if let Some ( s) = value. downcast_ref :: < PyStr > ( ) {
1443- // Create wchar_t buffer with null terminator and leak it
1444- let mut wchars: Vec < libc:: wchar_t > = s
1445- . as_str ( )
1446- . chars ( )
1447- . map ( |c| c as libc:: wchar_t )
1448- . chain ( std:: iter:: once ( 0 ) )
1449- . collect ( ) ;
1450- let ptr = wchars. as_mut_ptr ( ) ;
1451- std:: mem:: forget ( wchars) ; // Leak to keep the pointer valid
1452- let addr = ptr as usize ;
1486+ let ( holder, ptr) = str_to_wchar_bytes ( s. as_str ( ) , vm) ;
14531487 let mut result = vec ! [ 0u8 ; size] ;
1454- let addr_bytes = addr . to_ne_bytes ( ) ;
1488+ let addr_bytes = ptr . to_ne_bytes ( ) ;
14551489 let len = std:: cmp:: min ( addr_bytes. len ( ) , size) ;
14561490 result[ ..len] . copy_from_slice ( & addr_bytes[ ..len] ) ;
1457- return Ok ( result) ;
1491+ return Ok ( ( result, Some ( holder ) ) ) ;
14581492 }
14591493 // Integer address
14601494 if let Ok ( int_val) = value. try_index ( vm) {
@@ -1463,13 +1497,13 @@ impl PyCField {
14631497 let bytes = v. to_ne_bytes ( ) ;
14641498 let len = std:: cmp:: min ( bytes. len ( ) , size) ;
14651499 result[ ..len] . copy_from_slice ( & bytes[ ..len] ) ;
1466- return Ok ( result) ;
1500+ return Ok ( ( result, None ) ) ;
14671501 }
14681502 // None -> NULL pointer
14691503 if vm. is_none ( value) {
1470- return Ok ( vec ! [ 0u8 ; size] ) ;
1504+ return Ok ( ( vec ! [ 0u8 ; size] , None ) ) ;
14711505 }
1472- PyCField :: value_to_bytes ( value, size, vm)
1506+ Ok ( ( PyCField :: value_to_bytes ( value, size, vm) ? , None ) )
14731507 }
14741508 "P" => {
14751509 // c_void_p: store integer as pointer
@@ -1479,15 +1513,15 @@ impl PyCField {
14791513 let bytes = v. to_ne_bytes ( ) ;
14801514 let len = std:: cmp:: min ( bytes. len ( ) , size) ;
14811515 result[ ..len] . copy_from_slice ( & bytes[ ..len] ) ;
1482- return Ok ( result) ;
1516+ return Ok ( ( result, None ) ) ;
14831517 }
14841518 // None -> NULL pointer
14851519 if vm. is_none ( value) {
1486- return Ok ( vec ! [ 0u8 ; size] ) ;
1520+ return Ok ( ( vec ! [ 0u8 ; size] , None ) ) ;
14871521 }
1488- PyCField :: value_to_bytes ( value, size, vm)
1522+ Ok ( ( PyCField :: value_to_bytes ( value, size, vm) ? , None ) )
14891523 }
1490- _ => PyCField :: value_to_bytes ( value, size, vm) ,
1524+ _ => Ok ( ( PyCField :: value_to_bytes ( value, size, vm) ? , None ) ) ,
14911525 }
14921526 }
14931527
0 commit comments