Skip to content

Commit 8556955

Browse files
committed
from buffer
1 parent 792c19f commit 8556955

File tree

7 files changed

+830
-7
lines changed

7 files changed

+830
-7
lines changed

Lib/ctypes/wintypes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# The most useful windows datatypes
22
import ctypes
33

4-
BYTE = ctypes.c_byte
4+
BYTE = ctypes.c_ubyte
55
WORD = ctypes.c_ushort
66
DWORD = ctypes.c_ulong
77

crates/vm/src/stdlib/ctypes.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,165 @@ pub(crate) mod _ctypes {
158158
}
159159
}
160160

161+
/// Get the size of a ctypes type from its type object
162+
pub fn get_size_from_type(cls: &PyTypeRef, vm: &VirtualMachine) -> PyResult<usize> {
163+
// Try to get _type_ attribute for simple types
164+
if let Ok(type_attr) = cls.as_object().get_attr("_type_", vm) {
165+
if let Ok(s) = type_attr.str(vm) {
166+
let s = s.to_string();
167+
if s.len() == 1 && SIMPLE_TYPE_CHARS.contains(s.as_str()) {
168+
return Ok(get_size(&s));
169+
}
170+
}
171+
}
172+
// Fall back to sizeof
173+
size_of(Either::A(cls.clone()), vm)
174+
}
175+
176+
/// Convert bytes to appropriate Python object based on ctypes type
177+
pub fn bytes_to_pyobject(
178+
cls: &PyTypeRef,
179+
bytes: &[u8],
180+
vm: &VirtualMachine,
181+
) -> PyResult<PyObjectRef> {
182+
// Try to get _type_ attribute
183+
if let Ok(type_attr) = cls.as_object().get_attr("_type_", vm) {
184+
if let Ok(s) = type_attr.str(vm) {
185+
let ty = s.to_string();
186+
return match ty.as_str() {
187+
"c" => {
188+
// c_char - single byte
189+
Ok(vm.ctx.new_bytes(bytes.to_vec()).into())
190+
}
191+
"b" => {
192+
// c_byte - signed char
193+
let val = if !bytes.is_empty() { bytes[0] as i8 } else { 0 };
194+
Ok(vm.ctx.new_int(val).into())
195+
}
196+
"B" => {
197+
// c_ubyte - unsigned char
198+
let val = if !bytes.is_empty() { bytes[0] } else { 0 };
199+
Ok(vm.ctx.new_int(val).into())
200+
}
201+
"h" => {
202+
// c_short
203+
let val = if bytes.len() >= 2 {
204+
i16::from_ne_bytes([bytes[0], bytes[1]])
205+
} else {
206+
0
207+
};
208+
Ok(vm.ctx.new_int(val).into())
209+
}
210+
"H" => {
211+
// c_ushort
212+
let val = if bytes.len() >= 2 {
213+
u16::from_ne_bytes([bytes[0], bytes[1]])
214+
} else {
215+
0
216+
};
217+
Ok(vm.ctx.new_int(val).into())
218+
}
219+
"i" | "l" => {
220+
// c_int, c_long (assuming 32-bit)
221+
let val = if bytes.len() >= 4 {
222+
i32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
223+
} else {
224+
0
225+
};
226+
Ok(vm.ctx.new_int(val).into())
227+
}
228+
"I" | "L" => {
229+
// c_uint, c_ulong (assuming 32-bit)
230+
let val = if bytes.len() >= 4 {
231+
u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
232+
} else {
233+
0
234+
};
235+
Ok(vm.ctx.new_int(val).into())
236+
}
237+
"q" => {
238+
// c_longlong
239+
let val = if bytes.len() >= 8 {
240+
i64::from_ne_bytes([
241+
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5],
242+
bytes[6], bytes[7],
243+
])
244+
} else {
245+
0
246+
};
247+
Ok(vm.ctx.new_int(val).into())
248+
}
249+
"Q" => {
250+
// c_ulonglong
251+
let val = if bytes.len() >= 8 {
252+
u64::from_ne_bytes([
253+
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5],
254+
bytes[6], bytes[7],
255+
])
256+
} else {
257+
0
258+
};
259+
Ok(vm.ctx.new_int(val).into())
260+
}
261+
"f" => {
262+
// c_float
263+
let val = if bytes.len() >= 4 {
264+
f32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
265+
} else {
266+
0.0
267+
};
268+
Ok(vm.ctx.new_float(val as f64).into())
269+
}
270+
"d" | "g" => {
271+
// c_double
272+
let val = if bytes.len() >= 8 {
273+
f64::from_ne_bytes([
274+
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5],
275+
bytes[6], bytes[7],
276+
])
277+
} else {
278+
0.0
279+
};
280+
Ok(vm.ctx.new_float(val).into())
281+
}
282+
"?" => {
283+
// c_bool
284+
let val = !bytes.is_empty() && bytes[0] != 0;
285+
Ok(vm.ctx.new_bool(val).into())
286+
}
287+
"P" | "z" | "Z" => {
288+
// Pointer types - return as integer address
289+
let val = if bytes.len() >= mem::size_of::<usize>() {
290+
let mut arr = [0u8; 8];
291+
arr[..bytes.len().min(8)].copy_from_slice(&bytes[..bytes.len().min(8)]);
292+
usize::from_ne_bytes(arr)
293+
} else {
294+
0
295+
};
296+
Ok(vm.ctx.new_int(val).into())
297+
}
298+
"u" => {
299+
// c_wchar - wide character
300+
let val = if bytes.len() >= mem::size_of::<WideChar>() {
301+
let wc = if mem::size_of::<WideChar>() == 2 {
302+
u16::from_ne_bytes([bytes[0], bytes[1]]) as u32
303+
} else {
304+
u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
305+
};
306+
char::from_u32(wc).unwrap_or('\0')
307+
} else {
308+
'\0'
309+
};
310+
Ok(vm.ctx.new_str(val.to_string()).into())
311+
}
312+
_ => Ok(vm.ctx.none()),
313+
};
314+
}
315+
}
316+
// Default: return bytes as-is
317+
Ok(vm.ctx.new_bytes(bytes.to_vec()).into())
318+
}
319+
161320
const SIMPLE_TYPE_CHARS: &str = "cbBhHiIlLdfguzZPqQ?O";
162321

163322
pub fn new_simple_type(

crates/vm/src/stdlib/ctypes/array.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,170 @@ impl PyCArray {
393393
Err(vm.new_type_error("expected bytes".to_owned()))
394394
}
395395
}
396+
397+
#[pyclassmethod]
398+
fn from_address(cls: PyTypeRef, address: isize, vm: &VirtualMachine) -> PyResult {
399+
use crate::function::Either;
400+
use crate::stdlib::ctypes::_ctypes::size_of;
401+
402+
// Get size from cls
403+
let size = size_of(Either::A(cls.clone()), vm)?;
404+
405+
// Create instance with data from address
406+
if address != 0 && size > 0 {
407+
unsafe {
408+
let ptr = address as *const u8;
409+
let bytes = std::slice::from_raw_parts(ptr, size);
410+
// Get element type and length from cls
411+
let element_type = cls.as_object().get_attr("_type_", vm)?;
412+
let element_type: PyTypeRef = element_type
413+
.downcast()
414+
.map_err(|_| vm.new_type_error("_type_ must be a type".to_owned()))?;
415+
let length = cls
416+
.as_object()
417+
.get_attr("_length_", vm)?
418+
.try_int(vm)?
419+
.as_bigint()
420+
.to_usize()
421+
.unwrap_or(0);
422+
let element_size = if length > 0 { size / length } else { 0 };
423+
424+
Ok(PyCArray {
425+
typ: PyRwLock::new(element_type),
426+
length: AtomicCell::new(length),
427+
element_size: AtomicCell::new(element_size),
428+
buffer: PyRwLock::new(bytes.to_vec()),
429+
}
430+
.into_pyobject(vm))
431+
}
432+
} else {
433+
Err(vm.new_value_error("NULL pointer access".to_owned()))
434+
}
435+
}
436+
437+
#[pyclassmethod]
438+
fn from_buffer(
439+
cls: PyTypeRef,
440+
source: PyObjectRef,
441+
offset: crate::function::OptionalArg<isize>,
442+
vm: &VirtualMachine,
443+
) -> PyResult {
444+
use crate::TryFromObject;
445+
use crate::function::Either;
446+
use crate::protocol::PyBuffer;
447+
use crate::stdlib::ctypes::_ctypes::size_of;
448+
449+
let offset = offset.unwrap_or(0);
450+
if offset < 0 {
451+
return Err(vm.new_value_error("offset cannot be negative".to_owned()));
452+
}
453+
let offset = offset as usize;
454+
455+
// Get buffer from source
456+
let buffer = PyBuffer::try_from_object(vm, source.clone())?;
457+
458+
// Check if buffer is writable
459+
if buffer.desc.readonly {
460+
return Err(vm.new_type_error("underlying buffer is not writable".to_owned()));
461+
}
462+
463+
// Get size from cls
464+
let size = size_of(Either::A(cls.clone()), vm)?;
465+
466+
// Check if buffer is large enough
467+
let buffer_len = buffer.desc.len;
468+
if offset + size > buffer_len {
469+
return Err(vm.new_value_error(format!(
470+
"Buffer size too small ({} instead of at least {} bytes)",
471+
buffer_len,
472+
offset + size
473+
)));
474+
}
475+
476+
// Read bytes from buffer at offset
477+
let bytes = buffer.obj_bytes();
478+
let data = &bytes[offset..offset + size];
479+
480+
// Get element type and length from cls
481+
let element_type = cls.as_object().get_attr("_type_", vm)?;
482+
let element_type: PyTypeRef = element_type
483+
.downcast()
484+
.map_err(|_| vm.new_type_error("_type_ must be a type".to_owned()))?;
485+
let length = cls
486+
.as_object()
487+
.get_attr("_length_", vm)?
488+
.try_int(vm)?
489+
.as_bigint()
490+
.to_usize()
491+
.unwrap_or(0);
492+
let element_size = if length > 0 { size / length } else { 0 };
493+
494+
Ok(PyCArray {
495+
typ: PyRwLock::new(element_type),
496+
length: AtomicCell::new(length),
497+
element_size: AtomicCell::new(element_size),
498+
buffer: PyRwLock::new(data.to_vec()),
499+
}
500+
.into_pyobject(vm))
501+
}
502+
503+
#[pyclassmethod]
504+
fn from_buffer_copy(
505+
cls: PyTypeRef,
506+
source: crate::function::ArgBytesLike,
507+
offset: crate::function::OptionalArg<isize>,
508+
vm: &VirtualMachine,
509+
) -> PyResult {
510+
use crate::function::Either;
511+
use crate::stdlib::ctypes::_ctypes::size_of;
512+
513+
let offset = offset.unwrap_or(0);
514+
if offset < 0 {
515+
return Err(vm.new_value_error("offset cannot be negative".to_owned()));
516+
}
517+
let offset = offset as usize;
518+
519+
// Get size from cls
520+
let size = size_of(Either::A(cls.clone()), vm)?;
521+
522+
// Borrow bytes from source
523+
let source_bytes = source.borrow_buf();
524+
let buffer_len = source_bytes.len();
525+
526+
// Check if buffer is large enough
527+
if offset + size > buffer_len {
528+
return Err(vm.new_value_error(format!(
529+
"Buffer size too small ({} instead of at least {} bytes)",
530+
buffer_len,
531+
offset + size
532+
)));
533+
}
534+
535+
// Copy bytes from buffer at offset
536+
let data = &source_bytes[offset..offset + size];
537+
538+
// Get element type and length from cls
539+
let element_type = cls.as_object().get_attr("_type_", vm)?;
540+
let element_type: PyTypeRef = element_type
541+
.downcast()
542+
.map_err(|_| vm.new_type_error("_type_ must be a type".to_owned()))?;
543+
let length = cls
544+
.as_object()
545+
.get_attr("_length_", vm)?
546+
.try_int(vm)?
547+
.as_bigint()
548+
.to_usize()
549+
.unwrap_or(0);
550+
let element_size = if length > 0 { size / length } else { 0 };
551+
552+
Ok(PyCArray {
553+
typ: PyRwLock::new(element_type),
554+
length: AtomicCell::new(length),
555+
element_size: AtomicCell::new(element_size),
556+
buffer: PyRwLock::new(data.to_vec()),
557+
}
558+
.into_pyobject(vm))
559+
}
396560
}
397561

398562
impl PyCArray {

0 commit comments

Comments
 (0)