Skip to content

Commit fbbefed

Browse files
committed
pystruct::IntoStructFormatBytes to take str or bytes for format
1 parent 6070af3 commit fbbefed

File tree

2 files changed

+57
-31
lines changed

2 files changed

+57
-31
lines changed

vm/src/builtins/pystr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ impl PyStr {
283283
}
284284

285285
/// SAFETY: Given 'bytes' must be ascii
286-
unsafe fn new_ascii_unchecked(bytes: Vec<u8>) -> Self {
286+
pub(crate) unsafe fn new_ascii_unchecked(bytes: Vec<u8>) -> Self {
287287
Self::new_str_unchecked(bytes, PyStrKind::Ascii)
288288
}
289289

vm/src/stdlib/pystruct.rs

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
#[pymodule]
1313
pub(crate) mod _struct {
1414
use crate::{
15-
builtins::{float, PyBaseExceptionRef, PyBytesRef, PyStr, PyStrRef, PyTupleRef, PyTypeRef},
16-
common::str::wchar_t,
17-
function::{
18-
ArgAsciiBuffer, ArgBytesLike, ArgIntoBool, ArgMemoryBuffer, IntoPyObject, PosArgs,
15+
builtins::{
16+
float, PyBaseExceptionRef, PyBytes, PyBytesRef, PyStr, PyStrRef, PyTupleRef, PyTypeRef,
1917
},
18+
common::str::wchar_t,
19+
function::{ArgBytesLike, ArgIntoBool, ArgMemoryBuffer, IntoPyObject, PosArgs},
2020
protocol::PyIterReturn,
2121
slots::{IteratorIterable, SlotConstructor, SlotIterator},
22-
PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, VirtualMachine,
22+
PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, VirtualMachine,
2323
};
2424
use crossbeam_utils::atomic::AtomicCell;
2525
use half::f16;
@@ -203,6 +203,39 @@ pub(crate) mod _struct {
203203

204204
const OVERFLOW_MSG: &str = "total struct size too long";
205205

206+
struct IntoStructFormatBytes(PyStrRef);
207+
208+
impl TryFromObject for IntoStructFormatBytes {
209+
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
210+
// CPython turns str to bytes but we do reversed way here
211+
// The only performance difference is this transition cost
212+
let fmt = match_class! {
213+
match obj {
214+
s @ PyStr => if s.is_ascii() {
215+
Some(s)
216+
} else {
217+
None
218+
},
219+
b @ PyBytes => if b.is_ascii() {
220+
Some(unsafe {
221+
PyStr::new_ascii_unchecked(b.as_bytes().to_vec())
222+
}.into_ref(vm))
223+
} else {
224+
None
225+
},
226+
other => return Err(vm.new_type_error(format!("Struct() argument 1 must be a str or bytes object, not {}", other.class().name()))),
227+
}
228+
}.ok_or_else(|| vm.new_unicode_decode_error("Struct format must be a ascii string".to_owned()))?;
229+
Ok(IntoStructFormatBytes(fmt))
230+
}
231+
}
232+
233+
impl IntoStructFormatBytes {
234+
fn format_spec(&self, vm: &VirtualMachine) -> PyResult<FormatSpec> {
235+
FormatSpec::parse(self.0.as_str().as_bytes(), vm)
236+
}
237+
}
238+
206239
#[derive(Debug, Clone)]
207240
pub(crate) struct FormatSpec {
208241
endianness: Endianness,
@@ -708,20 +741,19 @@ pub(crate) mod _struct {
708741
}
709742

710743
#[pyfunction]
711-
fn pack(fmt: ArgAsciiBuffer, args: PosArgs, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
712-
let format_spec = fmt.with_ref(|bytes| FormatSpec::parse(bytes, vm))?;
713-
format_spec.pack(args.into_vec(), vm)
744+
fn pack(fmt: IntoStructFormatBytes, args: PosArgs, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
745+
fmt.format_spec(vm)?.pack(args.into_vec(), vm)
714746
}
715747

716748
#[pyfunction]
717749
fn pack_into(
718-
fmt: ArgAsciiBuffer,
750+
fmt: IntoStructFormatBytes,
719751
buffer: ArgMemoryBuffer,
720752
offset: isize,
721753
args: PosArgs,
722754
vm: &VirtualMachine,
723755
) -> PyResult<()> {
724-
let format_spec = fmt.with_ref(|bytes| FormatSpec::parse(bytes, vm))?;
756+
let format_spec = fmt.format_spec(vm)?;
725757
let offset = get_buffer_offset(buffer.len(), offset, format_spec.size, true, vm)?;
726758
buffer.with_ref(|data| format_spec.pack_into(&mut data[offset..], args.into_vec(), vm))
727759
}
@@ -744,11 +776,11 @@ pub(crate) mod _struct {
744776

745777
#[pyfunction]
746778
fn unpack(
747-
fmt: ArgAsciiBuffer,
779+
fmt: IntoStructFormatBytes,
748780
buffer: ArgBytesLike,
749781
vm: &VirtualMachine,
750782
) -> PyResult<PyTupleRef> {
751-
let format_spec = fmt.with_ref(|bytes| FormatSpec::parse(bytes, vm))?;
783+
let format_spec = fmt.format_spec(vm)?;
752784
buffer.with_ref(|buf| format_spec.unpack(buf, vm))
753785
}
754786

@@ -761,11 +793,11 @@ pub(crate) mod _struct {
761793

762794
#[pyfunction]
763795
fn unpack_from(
764-
fmt: ArgAsciiBuffer,
796+
fmt: IntoStructFormatBytes,
765797
args: UpdateFromArgs,
766798
vm: &VirtualMachine,
767799
) -> PyResult<PyTupleRef> {
768-
let format_spec = fmt.with_ref(|bytes| FormatSpec::parse(bytes, vm))?;
800+
let format_spec = fmt.format_spec(vm)?;
769801
let offset =
770802
get_buffer_offset(args.buffer.len(), args.offset, format_spec.size, false, vm)?;
771803
args.buffer
@@ -836,48 +868,42 @@ pub(crate) mod _struct {
836868

837869
#[pyfunction]
838870
fn iter_unpack(
839-
fmt: ArgAsciiBuffer,
871+
fmt: IntoStructFormatBytes,
840872
buffer: ArgBytesLike,
841873
vm: &VirtualMachine,
842874
) -> PyResult<UnpackIterator> {
843-
let format_spec = fmt.with_ref(|bytes| FormatSpec::parse(bytes, vm))?;
875+
let format_spec = fmt.format_spec(vm)?;
844876
UnpackIterator::new(vm, format_spec, buffer)
845877
}
846878

847879
#[pyfunction]
848-
fn calcsize(fmt: ArgAsciiBuffer, vm: &VirtualMachine) -> PyResult<usize> {
849-
let format_spec = fmt.with_ref(|bytes| FormatSpec::parse(bytes, vm))?;
850-
Ok(format_spec.size)
880+
fn calcsize(fmt: IntoStructFormatBytes, vm: &VirtualMachine) -> PyResult<usize> {
881+
Ok(fmt.format_spec(vm)?.size)
851882
}
852883

853884
#[pyattr]
854885
#[pyclass(name = "Struct")]
855886
#[derive(Debug, PyValue)]
856887
struct PyStruct {
857888
spec: FormatSpec,
858-
fmt_str: PyStrRef,
889+
format: PyStrRef,
859890
}
860891

861892
impl SlotConstructor for PyStruct {
862-
type Args = ArgAsciiBuffer;
893+
type Args = IntoStructFormatBytes;
863894

864895
fn py_new(cls: PyTypeRef, fmt: Self::Args, vm: &VirtualMachine) -> PyResult {
865-
let spec = fmt.with_ref(|bytes| FormatSpec::parse(bytes, vm))?;
866-
let fmt_str = match fmt {
867-
ArgAsciiBuffer::String(s) => s,
868-
buffer => buffer
869-
.with_ref(|bytes| PyStr::from(std::str::from_utf8(bytes).unwrap()))
870-
.into_ref(vm),
871-
};
872-
PyStruct { spec, fmt_str }.into_pyresult_with_type(vm, cls)
896+
let spec = fmt.format_spec(vm)?;
897+
let format = fmt.0;
898+
PyStruct { spec, format }.into_pyresult_with_type(vm, cls)
873899
}
874900
}
875901

876902
#[pyimpl(with(SlotConstructor))]
877903
impl PyStruct {
878904
#[pyproperty]
879905
fn format(&self) -> PyStrRef {
880-
self.fmt_str.clone()
906+
self.format.clone()
881907
}
882908

883909
#[pyproperty]

0 commit comments

Comments
 (0)