Skip to content

Commit ae8d64f

Browse files
committed
Fix several struct module tests.
1 parent 7129e76 commit ae8d64f

File tree

3 files changed

+83
-51
lines changed

3 files changed

+83
-51
lines changed

Lib/test/test_struct.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,6 @@ def __int__(self):
320320
t = IntTester(format)
321321
t.run()
322322

323-
# TODO: RUSTPYTHON
324-
@unittest.expectedFailure
325323
def test_nN_code(self):
326324
# n and N don't exist in standard sizes
327325
def assertStructError(func, *args, **kwargs):
@@ -352,7 +350,7 @@ def test_p_code(self):
352350
self.assertEqual(got, expectedback)
353351

354352
# TODO: RUSTPYTHON
355-
@unittest.skip("")
353+
@unittest.expectedFailure
356354
def test_705836(self):
357355
# SF bug 705836. "<f" and ">f" had a severe rounding bug, where a carry
358356
# from the low-order discarded bits could propagate into the exponent
@@ -394,8 +392,6 @@ def test_1530559(self):
394392
self.assertRaises(struct.error, struct.pack, 'P', 1.0)
395393
self.assertRaises(struct.error, struct.pack, 'P', 1.5)
396394

397-
# TODO: RUSTPYTHON
398-
@unittest.expectedFailure
399395
def test_unpack_from(self):
400396
test_string = b'abcd01234'
401397
fmt = '4s'
@@ -527,16 +523,15 @@ def __bool__(self):
527523
for c in [b'\x01', b'\x7f', b'\xff', b'\x0f', b'\xf0']:
528524
self.assertTrue(struct.unpack('>?', c)[0])
529525

530-
@unittest.skip("TODO: RUSTPYTHON")
526+
# TODO: RUSTPYTHON
527+
@unittest.expectedFailure
531528
def test_count_overflow(self):
532529
hugecount = '{}b'.format(sys.maxsize+1)
533530
self.assertRaises(struct.error, struct.calcsize, hugecount)
534531

535532
hugecount2 = '{}b{}H'.format(sys.maxsize//2, sys.maxsize//2)
536533
self.assertRaises(struct.error, struct.calcsize, hugecount2)
537534

538-
# TODO: RUSTPYTHON
539-
@unittest.expectedFailure
540535
def test_trailing_counter(self):
541536
store = array.array('b', b' '*100)
542537

@@ -676,8 +671,6 @@ class UnpackIteratorTest(unittest.TestCase):
676671
Tests for iterative unpacking (struct.Struct.iter_unpack).
677672
"""
678673

679-
# TODO: RUSTPYTHON
680-
@unittest.expectedFailure
681674
def test_construct(self):
682675
def _check_iterator(it):
683676
self.assertIsInstance(it, abc.Iterator)
@@ -720,8 +713,6 @@ def test_arbitrary_buffer(self):
720713
self.assertRaises(StopIteration, next, it)
721714
self.assertRaises(StopIteration, next, it)
722715

723-
# TODO: RUSTPYTHON
724-
@unittest.expectedFailure
725716
def test_length_hint(self):
726717
lh = operator.length_hint
727718
s = struct.Struct('>IB')

vm/src/byteslike.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::pyobject::{PyResult, TryFromObject, TypeProtocol};
55
use crate::stdlib::array::{PyArray, PyArrayRef};
66
use crate::vm::VirtualMachine;
77

8+
#[derive(Debug)]
89
pub enum PyBytesLike {
910
Bytes(PyBytesRef),
1011
Bytearray(PyByteArrayRef),
@@ -26,6 +27,18 @@ impl TryFromObject for PyBytesLike {
2627
}
2728

2829
impl PyBytesLike {
30+
pub fn len(&self) -> usize {
31+
match self {
32+
PyBytesLike::Bytes(b) => b.len(),
33+
PyBytesLike::Bytearray(b) => b.borrow_value().len(),
34+
PyBytesLike::Array(array) => array.len(),
35+
}
36+
}
37+
38+
pub fn is_empty(&self) -> bool {
39+
self.len() == 0
40+
}
41+
2942
pub fn to_cow(&self) -> std::borrow::Cow<[u8]> {
3043
match self {
3144
PyBytesLike::Bytes(b) => b.get_value().into(),

vm/src/stdlib/pystruct.rs

Lines changed: 67 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ mod _struct {
2121
use std::io::{Cursor, Read, Write};
2222
use std::iter::Peekable;
2323

24-
use crate::byteslike::PyBuffer;
24+
use crate::byteslike::{PyBuffer, PyBytesLike};
2525
use crate::exceptions::PyBaseExceptionRef;
2626
use crate::function::Args;
2727
use crate::obj::{
@@ -100,11 +100,11 @@ mod _struct {
100100
fn parse(fmt: &str) -> Result<FormatSpec, String> {
101101
let mut chars = fmt.chars().peekable();
102102

103-
// First determine "<", ">","!" or "="
104-
let endianness = parse_endiannes(&mut chars);
103+
// First determine "@", "<", ">","!" or "="
104+
let (is_native_size, endianness) = parse_size_and_endiannes(&mut chars);
105105

106106
// Now, analyze struct string furter:
107-
let codes = parse_format_codes(&mut chars)?;
107+
let codes = parse_format_codes(&mut chars, is_native_size)?;
108108

109109
Ok(FormatSpec { endianness, codes })
110110
}
@@ -190,36 +190,36 @@ mod _struct {
190190

191191
/// Parse endianness
192192
/// See also: https://docs.python.org/3/library/struct.html?highlight=struct#byte-order-size-and-alignment
193-
fn parse_endiannes<I>(chars: &mut Peekable<I>) -> Endianness
193+
fn parse_size_and_endiannes<I>(chars: &mut Peekable<I>) -> (bool, Endianness)
194194
where
195195
I: Sized + Iterator<Item = char>,
196196
{
197197
match chars.peek() {
198198
Some('@') => {
199199
chars.next().unwrap();
200-
Endianness::Native
200+
(true, Endianness::Native)
201201
}
202202
Some('=') => {
203203
chars.next().unwrap();
204-
Endianness::Native
204+
(false, Endianness::Native)
205205
}
206206
Some('<') => {
207207
chars.next().unwrap();
208-
Endianness::Little
208+
(false, Endianness::Little)
209209
}
210210
Some('>') => {
211211
chars.next().unwrap();
212-
Endianness::Big
212+
(false, Endianness::Big)
213213
}
214214
Some('!') => {
215215
chars.next().unwrap();
216-
Endianness::Network
216+
(false, Endianness::Network)
217217
}
218-
_ => Endianness::Native,
218+
_ => (true, Endianness::Native),
219219
}
220220
}
221221

222-
fn parse_format_codes<I>(chars: &mut Peekable<I>) -> Result<Vec<FormatCode>, String>
222+
fn parse_format_codes<I>(chars: &mut Peekable<I>, is_native_size: bool) -> Result<Vec<FormatCode>, String>
223223
where
224224
I: Sized + Iterator<Item = char>,
225225
{
@@ -243,6 +243,9 @@ mod _struct {
243243
// determine format char:
244244
let c = chars.next();
245245
match c {
246+
Some('n') | Some('N') if !is_native_size => {
247+
return Err("bad char in struct format".to_owned())
248+
}
246249
Some(c) if is_supported_format_character(c) => {
247250
codes.push(FormatCode { repeat, code: c })
248251
}
@@ -667,11 +670,11 @@ mod _struct {
667670
#[pyfunction]
668671
fn unpack(
669672
fmt: Either<PyStringRef, PyBytesRef>,
670-
buffer: PyBytesRef,
673+
buffer: PyBytesLike,
671674
vm: &VirtualMachine,
672675
) -> PyResult<PyTuple> {
673676
let format_spec = FormatSpec::decode_and_parse(vm, &fmt)?;
674-
format_spec.unpack(buffer.get_value(), vm)
677+
buffer.with_ref(|buf| format_spec.unpack(buf, vm))
675678
}
676679

677680
fn unpack_code<Endianness>(
@@ -721,26 +724,49 @@ mod _struct {
721724
Ok(())
722725
}
723726

727+
#[derive(FromArgs)]
728+
struct UpdateFromArgs {
729+
buffer: PyBytesLike,
730+
#[pyarg(positional_or_keyword, default = "0")]
731+
offset: isize
732+
}
733+
724734
#[pyfunction]
725735
fn unpack_from(
726736
fmt: Either<PyStringRef, PyBytesRef>,
727-
buffer: PyBytesRef,
728-
offset: isize,
737+
args: UpdateFromArgs,
729738
vm: &VirtualMachine,
730739
) -> PyResult<PyTuple> {
731740
let format_spec = FormatSpec::decode_and_parse(vm, &fmt)?;
732-
let offset = get_buffer_offset(buffer.len(), offset, format_spec.size(), vm)?;
733-
format_spec.unpack(&buffer.get_value()[offset..], vm)
741+
let size = format_spec.size();
742+
let offset = get_buffer_offset(args.buffer.len(), args.offset, size, vm)?;
743+
args.buffer.with_ref(|buf| format_spec.unpack(&buf[offset..offset+size], vm))
734744
}
735745

736746
#[pyclass(name = "unpack_iterator")]
737747
#[derive(Debug)]
738748
struct UnpackIterator {
739749
format_spec: FormatSpec,
740-
buffer: PyBytesRef,
750+
buffer: PyBytesLike,
741751
offset: AtomicCell<usize>,
742752
}
743753

754+
impl UnpackIterator {
755+
fn new(vm: &VirtualMachine, format_spec: FormatSpec, buffer: PyBytesLike) -> PyResult<UnpackIterator> {
756+
if format_spec.size() == 0 {
757+
Err(new_struct_error(vm, "cannot iteratively unpack with a struct of length 0".to_owned()))
758+
} else if buffer.len() % format_spec.size() != 0 {
759+
Err(new_struct_error(vm, format!("iterative unpacking requires a buffer of a multiple of {} bytes", format_spec.size())))
760+
} else {
761+
Ok(UnpackIterator {
762+
format_spec,
763+
buffer,
764+
offset: AtomicCell::new(0),
765+
})
766+
}
767+
}
768+
}
769+
744770
impl PyValue for UnpackIterator {
745771
fn class(vm: &VirtualMachine) -> PyClassRef {
746772
vm.class("_struct", "unpack_iterator")
@@ -756,24 +782,30 @@ mod _struct {
756782
if offset + size > self.buffer.len() {
757783
Err(objiter::new_stop_iteration(vm))
758784
} else {
759-
self.format_spec
760-
.unpack(&self.buffer.get_value()[offset..offset + size], vm)
785+
self.buffer.with_ref(|buf| self.format_spec
786+
.unpack(&buf[offset..offset + size], vm))
761787
}
762788
}
789+
790+
#[pymethod(magic)]
791+
fn iter(zelf: PyRef<Self>) -> PyRef<Self> {
792+
zelf
793+
}
794+
795+
#[pymethod(magic)]
796+
fn length_hint(&self) -> usize {
797+
self.buffer.len().saturating_sub(self.offset.load()) / self.format_spec.size()
798+
}
763799
}
764800

765801
#[pyfunction]
766802
fn iter_unpack(
767803
fmt: Either<PyStringRef, PyBytesRef>,
768-
buffer: PyBytesRef,
804+
buffer: PyBytesLike,
769805
vm: &VirtualMachine,
770806
) -> PyResult<UnpackIterator> {
771807
let format_spec = FormatSpec::decode_and_parse(vm, &fmt)?;
772-
Ok(UnpackIterator {
773-
format_spec,
774-
buffer,
775-
offset: AtomicCell::new(0),
776-
})
808+
UnpackIterator::new(vm, format_spec, buffer)
777809
}
778810

779811
#[pyfunction]
@@ -843,28 +875,24 @@ mod _struct {
843875
}
844876

845877
#[pymethod]
846-
fn unpack(&self, data: PyBytesRef, vm: &VirtualMachine) -> PyResult<PyTuple> {
847-
self.spec.unpack(data.get_value(), vm)
878+
fn unpack(&self, data: PyBytesLike, vm: &VirtualMachine) -> PyResult<PyTuple> {
879+
data.with_ref(|buf| self.spec.unpack(buf, vm))
848880
}
849881

850882
#[pymethod]
851883
fn unpack_from(
852884
&self,
853-
buffer: PyBytesRef,
854-
offset: isize,
885+
args: UpdateFromArgs,
855886
vm: &VirtualMachine,
856887
) -> PyResult<PyTuple> {
857-
let offset = get_buffer_offset(buffer.len(), offset, self.size(), vm)?;
858-
self.spec.unpack(&buffer.get_value()[offset..], vm)
888+
let size = self.size();
889+
let offset = get_buffer_offset(args.buffer.len(), args.offset, size, vm)?;
890+
args.buffer.with_ref(|buf| self.spec.unpack(&buf[offset..offset+size], vm))
859891
}
860892

861893
#[pymethod]
862-
fn iter_unpack(&self, buffer: PyBytesRef) -> PyResult<UnpackIterator> {
863-
Ok(UnpackIterator {
864-
format_spec: self.spec.clone(),
865-
buffer,
866-
offset: AtomicCell::new(0),
867-
})
894+
fn iter_unpack(&self, buffer: PyBytesLike, vm: &VirtualMachine) -> PyResult<UnpackIterator> {
895+
UnpackIterator::new(vm, self.spec.clone(), buffer)
868896
}
869897
}
870898

0 commit comments

Comments
 (0)