|
1 | 1 | use alloc::borrow::Cow; |
| 2 | +#[cfg(unix)] |
| 3 | +use alloc::ffi::CString; |
2 | 4 | use core::ffi::{ |
3 | 5 | CStr, c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, |
4 | 6 | c_ulong, c_ulonglong, c_ushort, c_void, |
@@ -32,8 +34,8 @@ use libloading::Library; |
32 | 34 | use libloading::os::unix::Library as UnixLibrary; |
33 | 35 | #[cfg(any(unix, windows))] |
34 | 36 | use parking_lot::{Mutex, RwLock}; |
35 | | -use rustpython_wtf8::Wtf8; |
36 | 37 | use rustpython_wtf8::Wtf8Buf; |
| 38 | +use rustpython_wtf8::{InteriorNulError, Wtf8}; |
37 | 39 | #[cfg(any(unix, windows))] |
38 | 40 | use std::{collections::HashMap, ffi::OsStr, sync::OnceLock}; |
39 | 41 |
|
@@ -383,7 +385,7 @@ pub fn dlopen_mode(load_flags: Option<i32>) -> i32 { |
383 | 385 |
|
384 | 386 | #[cfg(target_os = "macos")] |
385 | 387 | pub fn dyld_shared_cache_contains_path(path: &str) -> Result<bool, alloc::ffi::NulError> { |
386 | | - let c_path = alloc::ffi::CString::new(path)?; |
| 388 | + let c_path = CString::new(path)?; |
387 | 389 |
|
388 | 390 | unsafe extern "C" { |
389 | 391 | fn _dyld_shared_cache_contains_path(path: *const c_char) -> bool; |
@@ -523,21 +525,24 @@ pub fn encode_wtf8_to_wchar_padded(s: &Wtf8, size: usize) -> Vec<u8> { |
523 | 525 | wchar_bytes |
524 | 526 | } |
525 | 527 |
|
526 | | -pub fn wchar_null_terminated_bytes(s: &Wtf8) -> Vec<u8> { |
527 | | - let wchars: Vec<WChar> = s |
528 | | - .code_points() |
529 | | - .map(|cp| cp.to_u32() as WChar) |
530 | | - .chain(core::iter::once(0)) |
531 | | - .collect(); |
532 | | - vec_into_bytes(wchars) |
| 528 | +/// Encode `s` into a NUL terminated wide string without any interior NULs. |
| 529 | +/// |
| 530 | +/// The result of this function is suitable for Windows FFI. |
| 531 | +pub fn wchar_nul_terminated_bytes(s: &Wtf8) -> Result<Vec<u8>, InteriorNulError> { |
| 532 | + let v: Vec<u16> = s.encode_wide_ffi().collect::<Result<_, _>>()?; |
| 533 | + // SAFETY: u16 is POD. |
| 534 | + Ok(unsafe { vec_into_bytes(v) }) |
533 | 535 | } |
534 | 536 |
|
535 | | -pub fn vec_into_bytes<T>(vec: Vec<T>) -> Vec<u8> { |
536 | | - let len = vec.len() * core::mem::size_of::<T>(); |
537 | | - let cap = vec.capacity() * core::mem::size_of::<T>(); |
538 | | - let ptr = vec.as_ptr() as *mut u8; |
539 | | - core::mem::forget(vec); |
540 | | - unsafe { Vec::from_raw_parts(ptr, len, cap) } |
| 537 | +// Reinterpret Vec<T> into Vec<u8>. |
| 538 | +// |
| 539 | +// # Safety: |
| 540 | +// T must be a plain old data type. No pointers. No destructors. If this function ever needs to be |
| 541 | +// public, switch to using bytemuck instead. |
| 542 | +unsafe fn vec_into_bytes<T: Copy>(vec: Vec<T>) -> Vec<u8> { |
| 543 | + assert!(size_of::<T>() != 0); |
| 544 | + let (ptr, len, cap) = vec.into_raw_parts(); |
| 545 | + unsafe { Vec::from_raw_parts(ptr.cast::<u8>(), len * size_of::<T>(), cap * size_of::<T>()) } |
541 | 546 | } |
542 | 547 |
|
543 | 548 | pub enum IntegerValue { |
@@ -1122,14 +1127,16 @@ pub fn simple_storage_value_to_bytes_endian( |
1122 | 1127 | } |
1123 | 1128 | } |
1124 | 1129 |
|
1125 | | -pub fn utf16z_bytes(s: &Wtf8) -> Vec<u8> { |
1126 | | - vec_into_bytes::<u16>(s.encode_wide().chain(core::iter::once(0)).collect()) |
| 1130 | +#[inline] |
| 1131 | +pub fn utf16z_bytes(s: &Wtf8) -> Result<Vec<u8>, InteriorNulError> { |
| 1132 | + let v: Vec<u16> = s.encode_wide_ffi().collect::<Result<_, _>>()?; |
| 1133 | + // SAFETY: u16 is POD. |
| 1134 | + Ok(unsafe { vec_into_bytes::<u16>(v) }) |
1127 | 1135 | } |
1128 | 1136 |
|
1129 | | -pub fn null_terminated_bytes(bytes: &[u8]) -> Vec<u8> { |
1130 | | - let mut buffer = bytes.to_vec(); |
1131 | | - buffer.push(0); |
1132 | | - buffer |
| 1137 | +#[inline] |
| 1138 | +pub fn null_terminated_bytes(bytes: &[u8]) -> Result<Vec<u8>, alloc::ffi::NulError> { |
| 1139 | + CString::new(bytes).map(CString::into_bytes_with_nul) |
1133 | 1140 | } |
1134 | 1141 |
|
1135 | 1142 | pub fn decode_type_code(type_code: &str, bytes: &[u8]) -> DecodedValue { |
|
0 commit comments