From 4c537a46777f15b530b2470b12c4990af8aa25aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Sun, 24 Nov 2024 00:05:48 -0500 Subject: [PATCH] Fix some struct issues --- Src/IronPython.Modules/_struct.cs | 45 ++++++++++++++---------------- Src/StdLib/Lib/test/test_struct.py | 6 ++-- Tests/test_struct_stdlib.py | 10 +++---- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/Src/IronPython.Modules/_struct.cs b/Src/IronPython.Modules/_struct.cs index 7c7979063..c910d6e42 100644 --- a/Src/IronPython.Modules/_struct.cs +++ b/Src/IronPython.Modules/_struct.cs @@ -393,13 +393,13 @@ public void pack_into(CodeContext/*!*/ context, [NotNone] ByteArray/*!*/ buffer, } [Documentation("iteratively unpack the current format from the specified array.")] - public PythonUnpackIterator iter_unpack(CodeContext/*!*/ context, [BytesLike][NotNone] IList/*!*/ buffer, int offset = 0) { - return new PythonUnpackIterator(this, context, buffer, offset); + public PythonUnpackIterator iter_unpack(CodeContext/*!*/ context, [BytesLike][NotNone] IList/*!*/ buffer) { + return new PythonUnpackIterator(this, context, buffer); } [Documentation("iteratively unpack the current format from the specified array.")] - public PythonUnpackIterator iter_unpack(CodeContext/*!*/ context, [NotNone] ArrayModule.array/*!*/ buffer, int offset = 0) { - return new PythonUnpackIterator(this, context, buffer, offset); + public PythonUnpackIterator iter_unpack(CodeContext/*!*/ context, [NotNone] ArrayModule.array/*!*/ buffer) { + return new PythonUnpackIterator(this, context, buffer.ToByteArray()); } [Documentation("gets the number of bytes that the serialized string will occupy or are required to deserialize the data")] @@ -559,14 +559,19 @@ private static Struct CompileAndCache(CodeContext/*!*/ context, string/*!*/ fmt) fLittleEndian = false; fStandardized = true; break; + case '\x00': + throw Error(context, "embedded null character"); default: if (char.IsDigit(fmt[i])) { count = 0; while (char.IsDigit(fmt[i])) { count = count * 10 + (fmt[i] - '0'); i++; + if (i >= fmt.Length) { + throw Error(context, "repeat count given without format specifier"); + } } - if (char.IsWhiteSpace(fmt[i])) Error(context, "white space not allowed between count and format"); + if (char.IsWhiteSpace(fmt[i])) throw Error(context, "white space not allowed between count and format"); i--; break; } @@ -633,25 +638,13 @@ public class PythonUnpackIterator : System.Collections.IEnumerator, System.Colle private readonly CodeContext _context; private readonly IList _buffer; - private readonly int _start_offset; private readonly Struct _owner; private PythonUnpackIterator() { } - internal PythonUnpackIterator(Struct/*!*/ owner, CodeContext/*!*/ context, IList/*!*/ buffer, int offset) { + internal PythonUnpackIterator(Struct/*!*/ owner, CodeContext/*!*/ context, IList/*!*/ buffer) { _context = context; _buffer = buffer; - _start_offset = offset; - _owner = owner; - - Reset(); - ValidateBufferLength(); - } - - internal PythonUnpackIterator(Struct/*!*/ owner, CodeContext/*!*/ context, ArrayModule.array/*!*/ buffer, int offset) { - _context = context; - _buffer = buffer.ToByteArray(); - _start_offset = offset; _owner = owner; Reset(); @@ -659,7 +652,10 @@ internal PythonUnpackIterator(Struct/*!*/ owner, CodeContext/*!*/ context, Array } private void ValidateBufferLength() { - if (_buffer.Count - _start_offset < _owner.size) { + if (_owner.size == 0) { + throw Error(_context, "cannot iteratively unpack with a struct of length 0"); + } + if (_buffer.Count % _owner.size != 0) { throw Error(_context, $"iterative unpacking requires a buffer of a multiple of {_owner.size} bytes"); } } @@ -689,7 +685,7 @@ public bool MoveNext() { [PythonHidden] public void Reset() { _iter_current = null; - _next_offset = _start_offset; + _next_offset = 0; } #endregion @@ -869,13 +865,13 @@ public static void pack_into(CodeContext/*!*/ context, object fmt, [NotNone] Byt } [Documentation("Iteratively unpack the buffer, containing packed C structure data, according to\nfmt, starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt).")] - public static PythonUnpackIterator/*!*/ iter_unpack(CodeContext/*!*/ context, object fmt, [BytesLike][NotNone] IList/*!*/ buffer, int offset = 0) { - return GetStructFromCache(context, fmt).iter_unpack(context, buffer, offset); + public static PythonUnpackIterator/*!*/ iter_unpack(CodeContext/*!*/ context, object fmt, [BytesLike][NotNone] IList/*!*/ buffer) { + return GetStructFromCache(context, fmt).iter_unpack(context, buffer); } [Documentation("Iteratively unpack the buffer, containing packed C structure data, according to\nfmt, starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt).")] - public static PythonUnpackIterator/*!*/ iter_unpack(CodeContext/*!*/ context, object fmt, [NotNone] ArrayModule.array/*!*/ buffer, int offset = 0) { - return GetStructFromCache(context, fmt).iter_unpack(context, buffer, offset); + public static PythonUnpackIterator/*!*/ iter_unpack(CodeContext/*!*/ context, object fmt, [NotNone] ArrayModule.array/*!*/ buffer) { + return GetStructFromCache(context, fmt).iter_unpack(context, buffer); } #endregion @@ -1361,6 +1357,7 @@ internal static Bytes CreateString(CodeContext/*!*/ context, ref int index, int internal static Bytes CreatePascalString(CodeContext/*!*/ context, ref int index, int count, IList data) { int realLen = (int)data[index++]; + if (realLen > count) realLen = count; using var res = new MemoryStream(); for (int i = 0; i < realLen; i++) { res.WriteByte(data[index++]); diff --git a/Src/StdLib/Lib/test/test_struct.py b/Src/StdLib/Lib/test/test_struct.py index 0107eebca..866a70048 100644 --- a/Src/StdLib/Lib/test/test_struct.py +++ b/Src/StdLib/Lib/test/test_struct.py @@ -542,13 +542,13 @@ def test_trailing_counter(self): # format lists containing only count spec should result in an error self.assertRaises(struct.error, struct.pack, '12345') - self.assertRaises(struct.error, struct.unpack, '12345', '') + self.assertRaises(struct.error, struct.unpack, '12345', b'') self.assertRaises(struct.error, struct.pack_into, '12345', store, 0) self.assertRaises(struct.error, struct.unpack_from, '12345', store, 0) # Format lists with trailing count spec should result in an error self.assertRaises(struct.error, struct.pack, 'c12345', 'x') - self.assertRaises(struct.error, struct.unpack, 'c12345', 'x') + self.assertRaises(struct.error, struct.unpack, 'c12345', b'x') self.assertRaises(struct.error, struct.pack_into, 'c12345', store, 0, 'x') self.assertRaises(struct.error, struct.unpack_from, 'c12345', store, @@ -557,7 +557,7 @@ def test_trailing_counter(self): # Mixed format tests self.assertRaises(struct.error, struct.pack, '14s42', 'spam and eggs') self.assertRaises(struct.error, struct.unpack, '14s42', - 'spam and eggs') + b'spam and eggs') self.assertRaises(struct.error, struct.pack_into, '14s42', store, 0, 'spam and eggs') self.assertRaises(struct.error, struct.unpack_from, '14s42', store, 0) diff --git a/Tests/test_struct_stdlib.py b/Tests/test_struct_stdlib.py index 5acf05afa..bd23acb06 100644 --- a/Tests/test_struct_stdlib.py +++ b/Tests/test_struct_stdlib.py @@ -15,12 +15,10 @@ def load_tests(loader, standard_tests, pattern): if is_ironpython: failing_tests = [ - test.test_struct.StructTest('test_705836'), # TODO: figure out - test.test_struct.StructTest('test_bool'), # TODO: figure out - test.test_struct.StructTest('test_calcsize'), # TODO: figure out - test.test_struct.StructTest('test_count_overflow'), # TODO: figure out - test.test_struct.StructTest('test_trailing_counter'), # TODO: figure out - test.test_struct.UnpackIteratorTest('test_construct'), # TODO: figure out + test.test_struct.StructTest('test_705836'), # AssertionError: OverflowError not raised by pack + test.test_struct.StructTest('test_bool'), # struct.error: expected bool value got IronPython.NewTypes.System.Object_1$1 + test.test_struct.StructTest('test_calcsize'), # AssertionError: 4 not greater than or equal to 8 + test.test_struct.StructTest('test_count_overflow'), # AssertionError: error not raised by calcsize ] return generate_suite(tests, failing_tests)