mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
648 lines
20 KiB
Rust
648 lines
20 KiB
Rust
use crate::{
|
|
builtins::{PyBaseExceptionRef, PyBytesRef, PyTuple, PyTupleRef, PyTypeRef},
|
|
common::{static_cell, str::wchar_t},
|
|
convert::ToPyObject,
|
|
function::{ArgBytesLike, ArgIntoBool, ArgIntoFloat},
|
|
PyObjectRef, PyResult, TryFromObject, VirtualMachine,
|
|
};
|
|
use half::f16;
|
|
use itertools::Itertools;
|
|
use malachite_bigint::BigInt;
|
|
use num_traits::{PrimInt, ToPrimitive};
|
|
use std::{fmt, iter::Peekable, mem, os::raw};
|
|
|
|
type PackFunc = fn(&VirtualMachine, PyObjectRef, &mut [u8]) -> PyResult<()>;
|
|
type UnpackFunc = fn(&VirtualMachine, &[u8]) -> PyObjectRef;
|
|
|
|
static OVERFLOW_MSG: &str = "total struct size too long"; // not a const to reduce code size
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
pub(crate) enum Endianness {
|
|
Native,
|
|
Little,
|
|
Big,
|
|
Host,
|
|
}
|
|
|
|
impl Endianness {
|
|
/// Parse endianness
|
|
/// See also: https://docs.python.org/3/library/struct.html?highlight=struct#byte-order-size-and-alignment
|
|
fn parse<I>(chars: &mut Peekable<I>) -> Endianness
|
|
where
|
|
I: Sized + Iterator<Item = u8>,
|
|
{
|
|
let e = match chars.peek() {
|
|
Some(b'@') => Endianness::Native,
|
|
Some(b'=') => Endianness::Host,
|
|
Some(b'<') => Endianness::Little,
|
|
Some(b'>') | Some(b'!') => Endianness::Big,
|
|
_ => return Endianness::Native,
|
|
};
|
|
chars.next().unwrap();
|
|
e
|
|
}
|
|
}
|
|
|
|
trait ByteOrder {
|
|
fn convert<I: PrimInt>(i: I) -> I;
|
|
}
|
|
enum BigEndian {}
|
|
impl ByteOrder for BigEndian {
|
|
fn convert<I: PrimInt>(i: I) -> I {
|
|
i.to_be()
|
|
}
|
|
}
|
|
enum LittleEndian {}
|
|
impl ByteOrder for LittleEndian {
|
|
fn convert<I: PrimInt>(i: I) -> I {
|
|
i.to_le()
|
|
}
|
|
}
|
|
|
|
#[cfg(target_endian = "big")]
|
|
type NativeEndian = BigEndian;
|
|
#[cfg(target_endian = "little")]
|
|
type NativeEndian = LittleEndian;
|
|
|
|
#[derive(Copy, Clone, num_enum::TryFromPrimitive)]
|
|
#[repr(u8)]
|
|
pub(crate) enum FormatType {
|
|
Pad = b'x',
|
|
SByte = b'b',
|
|
UByte = b'B',
|
|
Char = b'c',
|
|
WideChar = b'u',
|
|
Str = b's',
|
|
Pascal = b'p',
|
|
Short = b'h',
|
|
UShort = b'H',
|
|
Int = b'i',
|
|
UInt = b'I',
|
|
Long = b'l',
|
|
ULong = b'L',
|
|
SSizeT = b'n',
|
|
SizeT = b'N',
|
|
LongLong = b'q',
|
|
ULongLong = b'Q',
|
|
Bool = b'?',
|
|
Half = b'e',
|
|
Float = b'f',
|
|
Double = b'd',
|
|
VoidP = b'P',
|
|
}
|
|
|
|
impl fmt::Debug for FormatType {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Debug::fmt(&(*self as u8 as char), f)
|
|
}
|
|
}
|
|
|
|
impl FormatType {
|
|
fn info(self, e: Endianness) -> &'static FormatInfo {
|
|
use mem::{align_of, size_of};
|
|
use FormatType::*;
|
|
macro_rules! native_info {
|
|
($t:ty) => {{
|
|
&FormatInfo {
|
|
size: size_of::<$t>(),
|
|
align: align_of::<$t>(),
|
|
pack: Some(<$t as Packable>::pack::<NativeEndian>),
|
|
unpack: Some(<$t as Packable>::unpack::<NativeEndian>),
|
|
}
|
|
}};
|
|
}
|
|
macro_rules! nonnative_info {
|
|
($t:ty, $end:ty) => {{
|
|
&FormatInfo {
|
|
size: size_of::<$t>(),
|
|
align: 0,
|
|
pack: Some(<$t as Packable>::pack::<$end>),
|
|
unpack: Some(<$t as Packable>::unpack::<$end>),
|
|
}
|
|
}};
|
|
}
|
|
macro_rules! match_nonnative {
|
|
($zelf:expr, $end:ty) => {{
|
|
match $zelf {
|
|
Pad | Str | Pascal => &FormatInfo {
|
|
size: size_of::<u8>(),
|
|
align: 0,
|
|
pack: None,
|
|
unpack: None,
|
|
},
|
|
SByte => nonnative_info!(i8, $end),
|
|
UByte => nonnative_info!(u8, $end),
|
|
Char => &FormatInfo {
|
|
size: size_of::<u8>(),
|
|
align: 0,
|
|
pack: Some(pack_char),
|
|
unpack: Some(unpack_char),
|
|
},
|
|
Short => nonnative_info!(i16, $end),
|
|
UShort => nonnative_info!(u16, $end),
|
|
Int | Long => nonnative_info!(i32, $end),
|
|
UInt | ULong => nonnative_info!(u32, $end),
|
|
LongLong => nonnative_info!(i64, $end),
|
|
ULongLong => nonnative_info!(u64, $end),
|
|
Bool => nonnative_info!(bool, $end),
|
|
Half => nonnative_info!(f16, $end),
|
|
Float => nonnative_info!(f32, $end),
|
|
Double => nonnative_info!(f64, $end),
|
|
_ => unreachable!(), // size_t or void*
|
|
}
|
|
}};
|
|
}
|
|
match e {
|
|
Endianness::Native => match self {
|
|
Pad | Str | Pascal => &FormatInfo {
|
|
size: size_of::<raw::c_char>(),
|
|
align: 0,
|
|
pack: None,
|
|
unpack: None,
|
|
},
|
|
SByte => native_info!(raw::c_schar),
|
|
UByte => native_info!(raw::c_uchar),
|
|
Char => &FormatInfo {
|
|
size: size_of::<raw::c_char>(),
|
|
align: 0,
|
|
pack: Some(pack_char),
|
|
unpack: Some(unpack_char),
|
|
},
|
|
WideChar => native_info!(wchar_t),
|
|
Short => native_info!(raw::c_short),
|
|
UShort => native_info!(raw::c_ushort),
|
|
Int => native_info!(raw::c_int),
|
|
UInt => native_info!(raw::c_uint),
|
|
Long => native_info!(raw::c_long),
|
|
ULong => native_info!(raw::c_ulong),
|
|
SSizeT => native_info!(isize), // ssize_t == isize
|
|
SizeT => native_info!(usize), // size_t == usize
|
|
LongLong => native_info!(raw::c_longlong),
|
|
ULongLong => native_info!(raw::c_ulonglong),
|
|
Bool => native_info!(bool),
|
|
Half => native_info!(f16),
|
|
Float => native_info!(raw::c_float),
|
|
Double => native_info!(raw::c_double),
|
|
VoidP => native_info!(*mut raw::c_void),
|
|
},
|
|
Endianness::Big => match_nonnative!(self, BigEndian),
|
|
Endianness::Little => match_nonnative!(self, LittleEndian),
|
|
Endianness::Host => match_nonnative!(self, NativeEndian),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub(crate) struct FormatCode {
|
|
pub repeat: usize,
|
|
pub code: FormatType,
|
|
pub info: &'static FormatInfo,
|
|
pub pre_padding: usize,
|
|
}
|
|
|
|
impl FormatCode {
|
|
pub fn arg_count(&self) -> usize {
|
|
match self.code {
|
|
FormatType::Pad => 0,
|
|
FormatType::Str | FormatType::Pascal => 1,
|
|
_ => self.repeat,
|
|
}
|
|
}
|
|
|
|
pub fn parse<I>(
|
|
chars: &mut Peekable<I>,
|
|
endianness: Endianness,
|
|
) -> Result<(Vec<Self>, usize, usize), String>
|
|
where
|
|
I: Sized + Iterator<Item = u8>,
|
|
{
|
|
let mut offset = 0isize;
|
|
let mut arg_count = 0usize;
|
|
let mut codes = vec![];
|
|
while chars.peek().is_some() {
|
|
// determine repeat operator:
|
|
let repeat = match chars.peek() {
|
|
Some(b'0'..=b'9') => {
|
|
let mut repeat = 0isize;
|
|
while let Some(b'0'..=b'9') = chars.peek() {
|
|
if let Some(c) = chars.next() {
|
|
let current_digit = c - b'0';
|
|
repeat = repeat
|
|
.checked_mul(10)
|
|
.and_then(|r| r.checked_add(current_digit as _))
|
|
.ok_or_else(|| OVERFLOW_MSG.to_owned())?;
|
|
}
|
|
}
|
|
repeat
|
|
}
|
|
_ => 1,
|
|
};
|
|
|
|
// determine format char:
|
|
let c = chars
|
|
.next()
|
|
.ok_or_else(|| "repeat count given without format specifier".to_owned())?;
|
|
let code = FormatType::try_from(c)
|
|
.ok()
|
|
.filter(|c| match c {
|
|
FormatType::SSizeT | FormatType::SizeT | FormatType::VoidP => {
|
|
endianness == Endianness::Native
|
|
}
|
|
_ => true,
|
|
})
|
|
.ok_or_else(|| "bad char in struct format".to_owned())?;
|
|
|
|
let info = code.info(endianness);
|
|
|
|
let padding = compensate_alignment(offset as usize, info.align)
|
|
.ok_or_else(|| OVERFLOW_MSG.to_owned())?;
|
|
offset = padding
|
|
.to_isize()
|
|
.and_then(|extra| offset.checked_add(extra))
|
|
.ok_or_else(|| OVERFLOW_MSG.to_owned())?;
|
|
|
|
let code = FormatCode {
|
|
repeat: repeat as usize,
|
|
code,
|
|
info,
|
|
pre_padding: padding,
|
|
};
|
|
arg_count += code.arg_count();
|
|
codes.push(code);
|
|
|
|
offset = (info.size as isize)
|
|
.checked_mul(repeat)
|
|
.and_then(|item_size| offset.checked_add(item_size))
|
|
.ok_or_else(|| OVERFLOW_MSG.to_owned())?;
|
|
}
|
|
|
|
Ok((codes, offset as usize, arg_count))
|
|
}
|
|
}
|
|
|
|
fn compensate_alignment(offset: usize, align: usize) -> Option<usize> {
|
|
if align != 0 && offset != 0 {
|
|
// a % b == a & (b-1) if b is a power of 2
|
|
(align - 1).checked_sub((offset - 1) & (align - 1))
|
|
} else {
|
|
// alignment is already all good
|
|
Some(0)
|
|
}
|
|
}
|
|
|
|
pub(crate) struct FormatInfo {
|
|
pub size: usize,
|
|
pub align: usize,
|
|
pub pack: Option<PackFunc>,
|
|
pub unpack: Option<UnpackFunc>,
|
|
}
|
|
impl fmt::Debug for FormatInfo {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("FormatInfo")
|
|
.field("size", &self.size)
|
|
.field("align", &self.align)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct FormatSpec {
|
|
#[allow(dead_code)]
|
|
pub(crate) endianness: Endianness,
|
|
pub(crate) codes: Vec<FormatCode>,
|
|
pub size: usize,
|
|
pub arg_count: usize,
|
|
}
|
|
|
|
impl FormatSpec {
|
|
pub fn parse(fmt: &[u8], vm: &VirtualMachine) -> PyResult<FormatSpec> {
|
|
let mut chars = fmt.iter().copied().peekable();
|
|
|
|
// First determine "@", "<", ">","!" or "="
|
|
let endianness = Endianness::parse(&mut chars);
|
|
|
|
// Now, analyze struct string further:
|
|
let (codes, size, arg_count) =
|
|
FormatCode::parse(&mut chars, endianness).map_err(|err| new_struct_error(vm, err))?;
|
|
|
|
Ok(FormatSpec {
|
|
endianness,
|
|
codes,
|
|
size,
|
|
arg_count,
|
|
})
|
|
}
|
|
|
|
pub fn pack(&self, args: Vec<PyObjectRef>, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
|
|
// Create data vector:
|
|
let mut data = vec![0; self.size];
|
|
|
|
self.pack_into(&mut data, args, vm)?;
|
|
|
|
Ok(data)
|
|
}
|
|
|
|
pub fn pack_into(
|
|
&self,
|
|
mut buffer: &mut [u8],
|
|
args: Vec<PyObjectRef>,
|
|
vm: &VirtualMachine,
|
|
) -> PyResult<()> {
|
|
if self.arg_count != args.len() {
|
|
return Err(new_struct_error(
|
|
vm,
|
|
format!(
|
|
"pack expected {} items for packing (got {})",
|
|
self.codes.len(),
|
|
args.len()
|
|
),
|
|
));
|
|
}
|
|
|
|
let mut args = args.into_iter();
|
|
// Loop over all opcodes:
|
|
for code in &self.codes {
|
|
buffer = &mut buffer[code.pre_padding..];
|
|
debug!("code: {:?}", code);
|
|
match code.code {
|
|
FormatType::Str => {
|
|
let (buf, rest) = buffer.split_at_mut(code.repeat);
|
|
pack_string(vm, args.next().unwrap(), buf)?;
|
|
buffer = rest;
|
|
}
|
|
FormatType::Pascal => {
|
|
let (buf, rest) = buffer.split_at_mut(code.repeat);
|
|
pack_pascal(vm, args.next().unwrap(), buf)?;
|
|
buffer = rest;
|
|
}
|
|
FormatType::Pad => {
|
|
let (pad_buf, rest) = buffer.split_at_mut(code.repeat);
|
|
for el in pad_buf {
|
|
*el = 0
|
|
}
|
|
buffer = rest;
|
|
}
|
|
_ => {
|
|
let pack = code.info.pack.unwrap();
|
|
for arg in args.by_ref().take(code.repeat) {
|
|
let (item_buf, rest) = buffer.split_at_mut(code.info.size);
|
|
pack(vm, arg, item_buf)?;
|
|
buffer = rest;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn unpack(&self, mut data: &[u8], vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
|
if self.size != data.len() {
|
|
return Err(new_struct_error(
|
|
vm,
|
|
format!("unpack requires a buffer of {} bytes", self.size),
|
|
));
|
|
}
|
|
|
|
let mut items = Vec::with_capacity(self.arg_count);
|
|
for code in &self.codes {
|
|
data = &data[code.pre_padding..];
|
|
debug!("unpack code: {:?}", code);
|
|
match code.code {
|
|
FormatType::Pad => {
|
|
data = &data[code.repeat..];
|
|
}
|
|
FormatType::Str => {
|
|
let (str_data, rest) = data.split_at(code.repeat);
|
|
// string is just stored inline
|
|
items.push(vm.ctx.new_bytes(str_data.to_vec()).into());
|
|
data = rest;
|
|
}
|
|
FormatType::Pascal => {
|
|
let (str_data, rest) = data.split_at(code.repeat);
|
|
items.push(unpack_pascal(vm, str_data));
|
|
data = rest;
|
|
}
|
|
_ => {
|
|
let unpack = code.info.unpack.unwrap();
|
|
for _ in 0..code.repeat {
|
|
let (item_data, rest) = data.split_at(code.info.size);
|
|
items.push(unpack(vm, item_data));
|
|
data = rest;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
Ok(PyTuple::new_ref(items, &vm.ctx))
|
|
}
|
|
|
|
#[inline]
|
|
pub fn size(&self) -> usize {
|
|
self.size
|
|
}
|
|
}
|
|
|
|
trait Packable {
|
|
fn pack<E: ByteOrder>(vm: &VirtualMachine, arg: PyObjectRef, data: &mut [u8]) -> PyResult<()>;
|
|
fn unpack<E: ByteOrder>(vm: &VirtualMachine, data: &[u8]) -> PyObjectRef;
|
|
}
|
|
|
|
trait PackInt: PrimInt {
|
|
fn pack_int<E: ByteOrder>(self, data: &mut [u8]);
|
|
fn unpack_int<E: ByteOrder>(data: &[u8]) -> Self;
|
|
}
|
|
|
|
macro_rules! make_pack_primint {
|
|
($T:ty) => {
|
|
impl PackInt for $T {
|
|
fn pack_int<E: ByteOrder>(self, data: &mut [u8]) {
|
|
let i = E::convert(self);
|
|
data.copy_from_slice(&i.to_ne_bytes());
|
|
}
|
|
#[inline]
|
|
fn unpack_int<E: ByteOrder>(data: &[u8]) -> Self {
|
|
let mut x = [0; std::mem::size_of::<$T>()];
|
|
x.copy_from_slice(data);
|
|
E::convert(<$T>::from_ne_bytes(x))
|
|
}
|
|
}
|
|
|
|
impl Packable for $T {
|
|
fn pack<E: ByteOrder>(
|
|
vm: &VirtualMachine,
|
|
arg: PyObjectRef,
|
|
data: &mut [u8],
|
|
) -> PyResult<()> {
|
|
let i: $T = get_int_or_index(vm, arg)?;
|
|
i.pack_int::<E>(data);
|
|
Ok(())
|
|
}
|
|
|
|
fn unpack<E: ByteOrder>(vm: &VirtualMachine, rdr: &[u8]) -> PyObjectRef {
|
|
let i = <$T>::unpack_int::<E>(rdr);
|
|
vm.ctx.new_int(i).into()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
fn get_int_or_index<T>(vm: &VirtualMachine, arg: PyObjectRef) -> PyResult<T>
|
|
where
|
|
T: PrimInt + for<'a> TryFrom<&'a BigInt>,
|
|
{
|
|
let index = arg.try_index_opt(vm).unwrap_or_else(|| {
|
|
Err(new_struct_error(
|
|
vm,
|
|
"required argument is not an integer".to_owned(),
|
|
))
|
|
})?;
|
|
index
|
|
.try_to_primitive(vm)
|
|
.map_err(|_| new_struct_error(vm, "argument out of range".to_owned()))
|
|
}
|
|
|
|
make_pack_primint!(i8);
|
|
make_pack_primint!(u8);
|
|
make_pack_primint!(i16);
|
|
make_pack_primint!(u16);
|
|
make_pack_primint!(i32);
|
|
make_pack_primint!(u32);
|
|
make_pack_primint!(i64);
|
|
make_pack_primint!(u64);
|
|
make_pack_primint!(usize);
|
|
make_pack_primint!(isize);
|
|
|
|
macro_rules! make_pack_float {
|
|
($T:ty) => {
|
|
impl Packable for $T {
|
|
fn pack<E: ByteOrder>(
|
|
vm: &VirtualMachine,
|
|
arg: PyObjectRef,
|
|
data: &mut [u8],
|
|
) -> PyResult<()> {
|
|
let f = *ArgIntoFloat::try_from_object(vm, arg)? as $T;
|
|
f.to_bits().pack_int::<E>(data);
|
|
Ok(())
|
|
}
|
|
|
|
fn unpack<E: ByteOrder>(vm: &VirtualMachine, rdr: &[u8]) -> PyObjectRef {
|
|
let i = PackInt::unpack_int::<E>(rdr);
|
|
<$T>::from_bits(i).to_pyobject(vm)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
make_pack_float!(f32);
|
|
make_pack_float!(f64);
|
|
|
|
impl Packable for f16 {
|
|
fn pack<E: ByteOrder>(vm: &VirtualMachine, arg: PyObjectRef, data: &mut [u8]) -> PyResult<()> {
|
|
let f_64 = *ArgIntoFloat::try_from_object(vm, arg)?;
|
|
let f_16 = f16::from_f64(f_64);
|
|
if f_16.is_infinite() != f_64.is_infinite() {
|
|
return Err(vm.new_overflow_error("float too large to pack with e format".to_owned()));
|
|
}
|
|
f_16.to_bits().pack_int::<E>(data);
|
|
Ok(())
|
|
}
|
|
|
|
fn unpack<E: ByteOrder>(vm: &VirtualMachine, rdr: &[u8]) -> PyObjectRef {
|
|
let i = PackInt::unpack_int::<E>(rdr);
|
|
f16::from_bits(i).to_f64().to_pyobject(vm)
|
|
}
|
|
}
|
|
|
|
impl Packable for *mut raw::c_void {
|
|
fn pack<E: ByteOrder>(vm: &VirtualMachine, arg: PyObjectRef, data: &mut [u8]) -> PyResult<()> {
|
|
usize::pack::<E>(vm, arg, data)
|
|
}
|
|
|
|
fn unpack<E: ByteOrder>(vm: &VirtualMachine, rdr: &[u8]) -> PyObjectRef {
|
|
usize::unpack::<E>(vm, rdr)
|
|
}
|
|
}
|
|
|
|
impl Packable for bool {
|
|
fn pack<E: ByteOrder>(vm: &VirtualMachine, arg: PyObjectRef, data: &mut [u8]) -> PyResult<()> {
|
|
let v = *ArgIntoBool::try_from_object(vm, arg)? as u8;
|
|
v.pack_int::<E>(data);
|
|
Ok(())
|
|
}
|
|
|
|
fn unpack<E: ByteOrder>(vm: &VirtualMachine, rdr: &[u8]) -> PyObjectRef {
|
|
let i = u8::unpack_int::<E>(rdr);
|
|
vm.ctx.new_bool(i != 0).into()
|
|
}
|
|
}
|
|
|
|
fn pack_char(vm: &VirtualMachine, arg: PyObjectRef, data: &mut [u8]) -> PyResult<()> {
|
|
let v = PyBytesRef::try_from_object(vm, arg)?;
|
|
let ch = *v.as_bytes().iter().exactly_one().map_err(|_| {
|
|
new_struct_error(
|
|
vm,
|
|
"char format requires a bytes object of length 1".to_owned(),
|
|
)
|
|
})?;
|
|
data[0] = ch;
|
|
Ok(())
|
|
}
|
|
|
|
fn pack_string(vm: &VirtualMachine, arg: PyObjectRef, buf: &mut [u8]) -> PyResult<()> {
|
|
let b = ArgBytesLike::try_from_object(vm, arg)?;
|
|
b.with_ref(|data| write_string(buf, data));
|
|
Ok(())
|
|
}
|
|
|
|
fn pack_pascal(vm: &VirtualMachine, arg: PyObjectRef, buf: &mut [u8]) -> PyResult<()> {
|
|
if buf.is_empty() {
|
|
return Ok(());
|
|
}
|
|
let b = ArgBytesLike::try_from_object(vm, arg)?;
|
|
b.with_ref(|data| {
|
|
let string_length = std::cmp::min(std::cmp::min(data.len(), 255), buf.len() - 1);
|
|
buf[0] = string_length as u8;
|
|
write_string(&mut buf[1..], data);
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
fn write_string(buf: &mut [u8], data: &[u8]) {
|
|
let len_from_data = std::cmp::min(data.len(), buf.len());
|
|
buf[..len_from_data].copy_from_slice(&data[..len_from_data]);
|
|
for byte in &mut buf[len_from_data..] {
|
|
*byte = 0
|
|
}
|
|
}
|
|
|
|
fn unpack_char(vm: &VirtualMachine, data: &[u8]) -> PyObjectRef {
|
|
vm.ctx.new_bytes(vec![data[0]]).into()
|
|
}
|
|
|
|
fn unpack_pascal(vm: &VirtualMachine, data: &[u8]) -> PyObjectRef {
|
|
let (&len, data) = match data.split_first() {
|
|
Some(x) => x,
|
|
None => {
|
|
// cpython throws an internal SystemError here
|
|
return vm.ctx.new_bytes(vec![]).into();
|
|
}
|
|
};
|
|
let len = std::cmp::min(len as usize, data.len());
|
|
vm.ctx.new_bytes(data[..len].to_vec()).into()
|
|
}
|
|
|
|
// XXX: are those functions expected to be placed here?
|
|
pub fn struct_error_type(vm: &VirtualMachine) -> &'static PyTypeRef {
|
|
static_cell! {
|
|
static INSTANCE: PyTypeRef;
|
|
}
|
|
INSTANCE.get_or_init(|| vm.ctx.new_exception_type("struct", "error", None))
|
|
}
|
|
|
|
pub fn new_struct_error(vm: &VirtualMachine, msg: String) -> PyBaseExceptionRef {
|
|
// can't just STRUCT_ERROR.get().unwrap() cause this could be called before from buffer
|
|
// machinery, independent of whether _struct was ever imported
|
|
vm.new_exception_msg(struct_error_type(vm).clone(), msg)
|
|
}
|