diff --git a/src/core/IronPython.Modules/_struct.cs b/src/core/IronPython.Modules/_struct.cs index 2bc6260fc..57180a832 100644 --- a/src/core/IronPython.Modules/_struct.cs +++ b/src/core/IronPython.Modules/_struct.cs @@ -51,7 +51,7 @@ private void InitializeFrom(Struct s) { } internal Struct(CodeContext/*!*/ context, [NotNone] string/*!*/ fmt) { - __init__(context, fmt); + InitializeFromFormat(context, fmt); } #region Python construction @@ -66,8 +66,11 @@ public Struct([ParamDictionary] IDictionary kwArgs, [NotNone] pa [Documentation("initializes or re-initializes the compiled struct object with a new format")] public void __init__(CodeContext/*!*/ context, object fmt) { - format = FormatObjectToString(fmt); + InitializeFromFormat(context, FormatObjectToString(fmt)); + } + private void InitializeFromFormat(CodeContext context, string format) { + this.format = format; Struct s; bool gotIt; lock (_cache) { @@ -237,6 +240,7 @@ public void __init__(CodeContext/*!*/ context, object fmt) { public void pack_into(CodeContext/*!*/ context, [NotNone] ByteArray/*!*/ buffer, int offset, [NotNone] params object[] args) { var existing = buffer.UnsafeByteList; + offset = NormalizeOffset(context, offset, buffer.Count); if (offset + size > existing.Count) { throw Error(context, $"pack_into requires a buffer of at least {size} bytes"); } @@ -254,6 +258,7 @@ public void pack_into(CodeContext/*!*/ context, [NotNone] IBufferProtocol/*!*/ b var span = existing.AsSpan(); + offset = NormalizeOffset(context, offset, span.Length); if (offset + size > span.Length) { throw Error(context, $"pack_into requires a buffer of at least {size} bytes"); } @@ -449,7 +454,7 @@ private int NormalizeOffset(CodeContext context, int offset, int length) { } [Documentation("iteratively unpack the current format from the specified array.")] - public PythonUnpackIterator iter_unpack(CodeContext/*!*/ context, [BytesLike][NotNone] IList/*!*/ buffer) { + public PythonUnpackIterator iter_unpack(CodeContext/*!*/ context, [NotNone] Bytes/*!*/ buffer) { return new PythonUnpackIterator(this, context, buffer); } @@ -605,6 +610,10 @@ private static Struct CompileAndCache(CodeContext/*!*/ context, string/*!*/ fmt) break; case ' ': // white space, ignore case '\t': + case '\n': + case '\r': + case '\v': + case '\f': break; case '=': // native if (i != 0) throw Error(context, "unexpected byte order"); @@ -628,15 +637,17 @@ private static Struct CompileAndCache(CodeContext/*!*/ context, string/*!*/ fmt) throw Error(context, "embedded null character"); default: if (char.IsDigit(fmt[i])) { - count = 0; + long repeatCount = 0; while (char.IsDigit(fmt[i])) { - count = count * 10 + (fmt[i] - '0'); + repeatCount = repeatCount * 10 + (fmt[i] - '0'); + if (repeatCount > int.MaxValue) throw Error(context, "repeat count too large"); i++; if (i >= fmt.Length) { throw Error(context, "repeat count given without format specifier"); } } if (char.IsWhiteSpace(fmt[i])) throw Error(context, "white space not allowed between count and format"); + count = (int)repeatCount; i--; break; } @@ -652,7 +663,7 @@ private static Struct CompileAndCache(CodeContext/*!*/ context, string/*!*/ fmt) _isStandardized = fStandardized, _isLittleEndian = fLittleEndian, }; - s.InitCountAndSize(); + s.InitCountAndSize(context); lock (_cache) { _cache.Add(fmt, s); @@ -660,7 +671,7 @@ private static Struct CompileAndCache(CodeContext/*!*/ context, string/*!*/ fmt) return s; } - private void InitCountAndSize() { + private void InitCountAndSize(CodeContext context) { var encodingCount = 0; var encodingSize = 0; foreach (Format format in _formats) { @@ -671,13 +682,19 @@ private void InitCountAndSize() { encodingCount++; } } + if (encodingCount < 0) throw Error(context, "total struct size too long"); if (!_isStandardized) { // In native mode, align to {size}-byte boundaries encodingSize = Align(encodingSize, format.NativeSize); + if (encodingSize < 0) throw Error(context, "total struct size too long"); } - encodingSize += GetNativeSize(format.Type, _isStandardized) * format.Count; + try { + encodingSize = checked(encodingSize + GetNativeSize(format.Type, _isStandardized) * format.Count); + } catch (OverflowException) { + throw Error(context, "total struct size too long"); + } } _encodingCount = encodingCount; _encodingSize = encodingSize; @@ -763,8 +780,7 @@ public bool MoveNext() { if (_pythonBuffer is null) { _iter_current = _owner.unpack_from(_context, _buffer, _next_offset); - } - else { + } else { _iter_current = _owner.unpack(_context, _pythonBuffer.AsReadOnlySpan().Slice(_next_offset, _owner.size).ToArray()); } _next_offset += _owner.size; @@ -883,12 +899,14 @@ private readonly struct Format(FormatType type, int count) { private static CacheDict _cache = new CacheDict(MAX_CACHE_SIZE); private static string FormatObjectToString(object fmt) { - if (Converter.TryConvertToString(fmt, out string res)) { - return res; - } if (fmt is IList b) { return PythonOps.MakeString(b); } + if (Converter.TryConvertToString(fmt, out string res)) { + // replicate CPython behaviour: throws UnicodeEncodeError if non-ASCII + StringOps.DoEncodeAscii(res); + return res; + } throw PythonOps.TypeError("Struct() argument 1 must be a str or bytes object, not {0}", PythonOps.GetPythonTypeName(fmt)); } @@ -946,7 +964,7 @@ public static void pack_into(CodeContext/*!*/ context, object fmt, [NotNone] IBu } [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) { + public static PythonUnpackIterator/*!*/ iter_unpack(CodeContext/*!*/ context, object fmt, [NotNone] Bytes/*!*/ buffer) { return GetStructFromCache(context, fmt).iter_unpack(context, buffer); } @@ -976,15 +994,8 @@ private static void WriteShort(this MemoryStream res, bool fLittleEndian, short } } - private static void WriteUShort(this MemoryStream res, bool fLittleEndian, ushort val) { - if (fLittleEndian) { - res.WriteByte((byte)(val & 0xff)); - res.WriteByte((byte)((val >> 8) & 0xff)); - } else { - res.WriteByte((byte)((val >> 8) & 0xff)); - res.WriteByte((byte)(val & 0xff)); - } - } + private static void WriteUShort(this MemoryStream res, bool fLittleEndian, ushort val) + => WriteShort(res, fLittleEndian, unchecked((short)val)); private static void WriteInt(this MemoryStream res, bool fLittleEndian, int val) { if (fLittleEndian) { @@ -1000,13 +1011,24 @@ private static void WriteInt(this MemoryStream res, bool fLittleEndian, int val) } } - private static void WriteUInt(this MemoryStream res, bool fLittleEndian, uint val) { + private static void WriteUInt(this MemoryStream res, bool fLittleEndian, uint val) + => WriteInt(res, fLittleEndian, unchecked((int)val)); + + private static void WriteLong(this MemoryStream res, bool fLittleEndian, long val) { if (fLittleEndian) { res.WriteByte((byte)(val & 0xff)); res.WriteByte((byte)((val >> 8) & 0xff)); res.WriteByte((byte)((val >> 16) & 0xff)); res.WriteByte((byte)((val >> 24) & 0xff)); + res.WriteByte((byte)((val >> 32) & 0xff)); + res.WriteByte((byte)((val >> 40) & 0xff)); + res.WriteByte((byte)((val >> 48) & 0xff)); + res.WriteByte((byte)((val >> 56) & 0xff)); } else { + res.WriteByte((byte)((val >> 56) & 0xff)); + res.WriteByte((byte)((val >> 48) & 0xff)); + res.WriteByte((byte)((val >> 40) & 0xff)); + res.WriteByte((byte)((val >> 32) & 0xff)); res.WriteByte((byte)((val >> 24) & 0xff)); res.WriteByte((byte)((val >> 16) & 0xff)); res.WriteByte((byte)((val >> 8) & 0xff)); @@ -1014,6 +1036,9 @@ private static void WriteUInt(this MemoryStream res, bool fLittleEndian, uint va } } + private static void WriteULong(this MemoryStream res, bool fLittleEndian, ulong val) + => WriteLong(res, fLittleEndian, unchecked((long)val)); + private static void WritePointer(this MemoryStream res, bool fLittleEndian, ulong val) { if (UIntPtr.Size == 4) { res.WriteUInt(fLittleEndian, (uint)val); @@ -1031,88 +1056,15 @@ private static void WriteSignedNetPointer(this MemoryStream res, bool fLittleEnd } #if NET6_0_OR_GREATER - private static void WriteHalf(this MemoryStream res, bool fLittleEndian, Half val) { - byte[] bytes = BitConverter.GetBytes(val); - if (BitConverter.IsLittleEndian == fLittleEndian) { - res.Write(bytes, 0, bytes.Length); - } else { - res.WriteByte(bytes[1]); - res.WriteByte(bytes[0]); - } - } + private static void WriteHalf(this MemoryStream res, bool fLittleEndian, Half val) + => WriteShort(res, fLittleEndian, Unsafe.As(ref val)); #endif - private static void WriteFloat(this MemoryStream res, bool fLittleEndian, float val) { - byte[] bytes = BitConverter.GetBytes(val); - if (BitConverter.IsLittleEndian == fLittleEndian) { - res.Write(bytes, 0, bytes.Length); - } else { - res.WriteByte(bytes[3]); - res.WriteByte(bytes[2]); - res.WriteByte(bytes[1]); - res.WriteByte(bytes[0]); - } - } - - private static void WriteLong(this MemoryStream res, bool fLittleEndian, long val) { - if (fLittleEndian) { - res.WriteByte((byte)(val & 0xff)); - res.WriteByte((byte)((val >> 8) & 0xff)); - res.WriteByte((byte)((val >> 16) & 0xff)); - res.WriteByte((byte)((val >> 24) & 0xff)); - res.WriteByte((byte)((val >> 32) & 0xff)); - res.WriteByte((byte)((val >> 40) & 0xff)); - res.WriteByte((byte)((val >> 48) & 0xff)); - res.WriteByte((byte)((val >> 56) & 0xff)); - } else { - res.WriteByte((byte)((val >> 56) & 0xff)); - res.WriteByte((byte)((val >> 48) & 0xff)); - res.WriteByte((byte)((val >> 40) & 0xff)); - res.WriteByte((byte)((val >> 32) & 0xff)); - res.WriteByte((byte)((val >> 24) & 0xff)); - res.WriteByte((byte)((val >> 16) & 0xff)); - res.WriteByte((byte)((val >> 8) & 0xff)); - res.WriteByte((byte)(val & 0xff)); - } - } - - private static void WriteULong(this MemoryStream res, bool fLittleEndian, ulong val) { - if (fLittleEndian) { - res.WriteByte((byte)(val & 0xff)); - res.WriteByte((byte)((val >> 8) & 0xff)); - res.WriteByte((byte)((val >> 16) & 0xff)); - res.WriteByte((byte)((val >> 24) & 0xff)); - res.WriteByte((byte)((val >> 32) & 0xff)); - res.WriteByte((byte)((val >> 40) & 0xff)); - res.WriteByte((byte)((val >> 48) & 0xff)); - res.WriteByte((byte)((val >> 56) & 0xff)); - } else { - res.WriteByte((byte)((val >> 56) & 0xff)); - res.WriteByte((byte)((val >> 48) & 0xff)); - res.WriteByte((byte)((val >> 40) & 0xff)); - res.WriteByte((byte)((val >> 32) & 0xff)); - res.WriteByte((byte)((val >> 24) & 0xff)); - res.WriteByte((byte)((val >> 16) & 0xff)); - res.WriteByte((byte)((val >> 8) & 0xff)); - res.WriteByte((byte)(val & 0xff)); - } - } + private static void WriteFloat(this MemoryStream res, bool fLittleEndian, float val) + => WriteInt(res, fLittleEndian, Unsafe.As(ref val)); - private static void WriteDouble(this MemoryStream res, bool fLittleEndian, double val) { - byte[] bytes = BitConverter.GetBytes(val); - if (BitConverter.IsLittleEndian == fLittleEndian) { - res.Write(bytes, 0, bytes.Length); - } else { - res.WriteByte(bytes[7]); - res.WriteByte(bytes[6]); - res.WriteByte(bytes[5]); - res.WriteByte(bytes[4]); - res.WriteByte(bytes[3]); - res.WriteByte(bytes[2]); - res.WriteByte(bytes[1]); - res.WriteByte(bytes[0]); - } - } + private static void WriteDouble(this MemoryStream res, bool fLittleEndian, double val) + => WriteLong(res, fLittleEndian, Unsafe.As(ref val)); private static void WriteString(this MemoryStream res, int len, IList val) { for (int i = 0; i < val.Count && i < len; i++) { @@ -1124,6 +1076,7 @@ private static void WriteString(this MemoryStream res, int len, IList val) } private static void WritePascalString(this MemoryStream res, int len, IList val) { + if (len < 0) return; byte lenByte = (byte)Math.Min(255, Math.Min(val.Count, len)); res.WriteByte(lenByte); @@ -1140,12 +1093,7 @@ private static void WritePascalString(this MemoryStream res, int len, IList data) { - byte b1 = data[index++]; - byte b2 = data[index++]; + int b1 = data[index++]; + int b2 = data[index++]; if (fLittleEndian) { - return (short)((b2 << 8) | b1); + return unchecked((short)((b2 << 8) | b1)); } else { - return (short)((b1 << 8) | b2); + return unchecked((short)((b1 << 8) | b2)); } } - internal static ushort CreateUShortValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) { - byte b1 = data[index++]; - byte b2 = data[index++]; + internal static ushort CreateUShortValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) + => unchecked((ushort)CreateShortValue(context, ref index, fLittleEndian, data)); - if (fLittleEndian) { - return (ushort)((b2 << 8) | b1); - } else { - return (ushort)((b1 << 8) | b2); - } + internal static int CreateIntValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) { + int b1 = data[index++]; + int b2 = data[index++]; + int b3 = data[index++]; + int b4 = data[index++]; + + if (fLittleEndian) + return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; + else + return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; + } + + internal static uint CreateUIntValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) + => unchecked((uint)CreateIntValue(context, ref index, fLittleEndian, data)); + + internal static long CreateLongValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) { + long b1 = data[index++]; + long b2 = data[index++]; + long b3 = data[index++]; + long b4 = data[index++]; + long b5 = data[index++]; + long b6 = data[index++]; + long b7 = data[index++]; + long b8 = data[index++]; + + if (fLittleEndian) + return (b8 << 56) | (b7 << 48) | (b6 << 40) | (b5 << 32) | + (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; + else + return (b1 << 56) | (b2 << 48) | (b3 << 40) | (b4 << 32) | + (b5 << 24) | (b6 << 16) | (b7 << 8) | b8; } + internal static ulong CreateULongValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) + => unchecked((ulong)CreateLongValue(context, ref index, fLittleEndian, data)); + #if NET6_0_OR_GREATER internal static Half CreateHalfValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) { - byte[] bytes = new byte[2]; - if (fLittleEndian == BitConverter.IsLittleEndian) { - bytes[0] = data[index++]; - bytes[1] = data[index++]; - } else { - bytes[1] = data[index++]; - bytes[0] = data[index++]; - } - Half res = BitConverter.ToHalf(bytes, 0); + var val = CreateShortValue(context, ref index, fLittleEndian, data); + var res = Unsafe.As(ref val); if (context.LanguageContext.FloatFormat == FloatFormat.Unknown) { if (Half.IsNaN(res) || Half.IsInfinity(res)) { @@ -1362,19 +1331,8 @@ internal static Half CreateHalfValue(CodeContext/*!*/ context, ref int index, bo #endif internal static float CreateFloatValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) { - byte[] bytes = new byte[4]; - if (fLittleEndian == BitConverter.IsLittleEndian) { - bytes[0] = data[index++]; - bytes[1] = data[index++]; - bytes[2] = data[index++]; - bytes[3] = data[index++]; - } else { - bytes[3] = data[index++]; - bytes[2] = data[index++]; - bytes[1] = data[index++]; - bytes[0] = data[index++]; - } - float res = BitConverter.ToSingle(bytes, 0); + var val = CreateIntValue(context, ref index, fLittleEndian, data); + var res = Unsafe.As(ref val); if (context.LanguageContext.FloatFormat == FloatFormat.Unknown) { if (float.IsNaN(res) || float.IsInfinity(res)) { @@ -1385,88 +1343,10 @@ internal static float CreateFloatValue(CodeContext/*!*/ context, ref int index, return res; } - internal static int CreateIntValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) { - byte b1 = data[index++]; - byte b2 = data[index++]; - byte b3 = data[index++]; - byte b4 = data[index++]; - - if (fLittleEndian) - return (int)((b4 << 24) | (b3 << 16) | (b2 << 8) | b1); - else - return (int)((b1 << 24) | (b2 << 16) | (b3 << 8) | b4); - } - - internal static uint CreateUIntValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) { - byte b1 = data[index++]; - byte b2 = data[index++]; - byte b3 = data[index++]; - byte b4 = data[index++]; - - if (fLittleEndian) - return (uint)((b4 << 24) | (b3 << 16) | (b2 << 8) | b1); - else - return (uint)((b1 << 24) | (b2 << 16) | (b3 << 8) | b4); - } - - internal static long CreateLongValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) { - long b1 = data[index++]; - long b2 = data[index++]; - long b3 = data[index++]; - long b4 = data[index++]; - long b5 = data[index++]; - long b6 = data[index++]; - long b7 = data[index++]; - long b8 = data[index++]; - - if (fLittleEndian) - return (long)((b8 << 56) | (b7 << 48) | (b6 << 40) | (b5 << 32) | - (b4 << 24) | (b3 << 16) | (b2 << 8) | b1); - else - return (long)((b1 << 56) | (b2 << 48) | (b3 << 40) | (b4 << 32) | - (b5 << 24) | (b6 << 16) | (b7 << 8) | b8); - } - - internal static ulong CreateULongValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) { - ulong b1 = data[index++]; - ulong b2 = data[index++]; - ulong b3 = data[index++]; - ulong b4 = data[index++]; - ulong b5 = data[index++]; - ulong b6 = data[index++]; - ulong b7 = data[index++]; - ulong b8 = data[index++]; - if (fLittleEndian) - return (ulong)((b8 << 56) | (b7 << 48) | (b6 << 40) | (b5 << 32) | - (b4 << 24) | (b3 << 16) | (b2 << 8) | b1); - else - return (ulong)((b1 << 56) | (b2 << 48) | (b3 << 40) | (b4 << 32) | - (b5 << 24) | (b6 << 16) | (b7 << 8) | b8); - } - internal static double CreateDoubleValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, IList data) { - byte[] bytes = new byte[8]; - if (fLittleEndian == BitConverter.IsLittleEndian) { - bytes[0] = data[index++]; - bytes[1] = data[index++]; - bytes[2] = data[index++]; - bytes[3] = data[index++]; - bytes[4] = data[index++]; - bytes[5] = data[index++]; - bytes[6] = data[index++]; - bytes[7] = data[index++]; - } else { - bytes[7] = data[index++]; - bytes[6] = data[index++]; - bytes[5] = data[index++]; - bytes[4] = data[index++]; - bytes[3] = data[index++]; - bytes[2] = data[index++]; - bytes[1] = data[index++]; - bytes[0] = data[index++]; - } + var val = CreateLongValue(context, ref index, fLittleEndian, data); + var res = Unsafe.As(ref val); - double res = BitConverter.ToDouble(bytes, 0); if (context.LanguageContext.DoubleFormat == FloatFormat.Unknown) { if (double.IsNaN(res) || double.IsInfinity(res)) { throw PythonOps.ValueError("can't unpack IEEE 754 special value on non-IEEE platform"); @@ -1477,26 +1357,29 @@ internal static double CreateDoubleValue(CodeContext/*!*/ context, ref int index } internal static Bytes CreateString(CodeContext/*!*/ context, ref int index, int count, IList data) { - using var res = new MemoryStream(); + if (count == 0) return Bytes.Empty; + var res = new byte[count]; for (int i = 0; i < count; i++) { - res.WriteByte(data[index++]); + res[i] = data[index++]; } - return Bytes.Make(res.ToArray()); + return Bytes.Make(res); } internal static Bytes CreatePascalString(CodeContext/*!*/ context, ref int index, int count, IList data) { - int realLen = (int)data[index++]; + if (count < 0) return Bytes.Empty; + int realLen = data[index++]; + if (count == 0) return Bytes.Empty; if (realLen > count) realLen = count; - using var res = new MemoryStream(); + var res = new byte[realLen]; for (int i = 0; i < realLen; i++) { - res.WriteByte(data[index++]); + res[i] = data[index++]; } for (int i = realLen; i < count; i++) { // throw away null bytes index++; } - return Bytes.Make(res.ToArray()); + return Bytes.Make(res); } #endregion diff --git a/tests/suite/stdlib/test_struct.py b/tests/suite/stdlib/test_struct.py index fa9dc43d8..58e18f110 100644 --- a/tests/suite/stdlib/test_struct.py +++ b/tests/suite/stdlib/test_struct.py @@ -17,9 +17,7 @@ def load_tests(loader, standard_tests, pattern): if is_ironpython: failing_tests = [ - 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 ] if sys.version_info >= (3, 6): if net_version < (6, 0): diff --git a/tests/suite/test_struct.py b/tests/suite/test_struct.py index 890b1c679..a3b44abd5 100644 --- a/tests/suite/test_struct.py +++ b/tests/suite/test_struct.py @@ -49,6 +49,22 @@ def test_unpack_from(self): a, = struct.unpack_from('>H', memoryview(data), -2) self.assertEqual(a, 2) + def test_zero_length_string_fields(self): + self.assertEqual(struct.pack('0s', b'ignored'), b'') + self.assertEqual(struct.unpack('0s', b''), (b'',)) + self.assertEqual(struct.pack('0p', b'ignored'), b'') + self.assertEqual(struct.unpack('0p', b''), (b'',)) + + self.assertEqual(struct.pack('1pB', b'ignored', 7), b'\x00\x07') + self.assertEqual(struct.unpack('1pB', b'\x00\x07'), (b'', 7)) + + def test_format_string_ascii_and_whitespace(self): + self.assertEqual(struct.calcsize('b \tb\nb\rb\vb\fb'), 6) + with self.assertRaises(UnicodeEncodeError): + struct.calcsize(chr(0x0662) + 'b') + with self.assertRaises(struct.error): + struct.calcsize(bytes([0xff])) + def test_pack_into(self): # test string format string result = array.array('b', [0, 0]) @@ -65,6 +81,13 @@ def test_pack_into(self): struct.pack_into('>H', result, 0, 0xABCD) self.assertSequenceEqual(result, bytearray(b"\xAB\xCD")) + struct.pack_into('>H', result, -2, 0x1234) + self.assertSequenceEqual(result, bytearray(b"\x12\x34")) + + result = bytearray(b'\x00\x00') + struct.pack_into('>H', memoryview(result), -2, 0xABCD) + self.assertSequenceEqual(result, bytearray(b"\xAB\xCD")) + def test_ipy2_gh407(self): """https://github.com/IronLanguages/ironpython2/issues/407""" @@ -104,6 +127,12 @@ def test_iter_unpack(self): # struct.error: iterative unpacking requires a buffer of a multiple of {N} bytes self.assertRaises(struct.error, struct.iter_unpack, "h", b"\0") + mutable = bytearray(b"\x01\x02") + it = struct.iter_unpack('B', mutable) + with self.assertRaises(BufferError): + mutable.append(3) + self.assertEqual(list(it), [(1,), (2,)]) + def test_sizes(self): # test sizes of standard struct types