From 5de4e4e147191e987930b3bc24f0fe98dd503cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Thu, 5 Nov 2026 18:38:04 -0500 Subject: [PATCH 1/5] Changes from zlib.net --- src/core/IronPython.Modules/zlib/zlib.net/InfTree.cs | 8 ++++---- src/core/IronPython.Modules/zlib/zlib.net/Inflate.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/IronPython.Modules/zlib/zlib.net/InfTree.cs b/src/core/IronPython.Modules/zlib/zlib.net/InfTree.cs index 04edd208c..95c319e45 100644 --- a/src/core/IronPython.Modules/zlib/zlib.net/InfTree.cs +++ b/src/core/IronPython.Modules/zlib/zlib.net/InfTree.cs @@ -84,7 +84,7 @@ internal sealed class InfTree /// Given a list of code lengths and a maximum table size, make a set of /// tables to decode that set of codes. /// - /// Return (int)ZLibResultCode.Z_OK on success, (int)ZLibResultCode.Z_DATA_ERROR if the given code set is incomplete (the tables are still built in this case), (int)ZLibResultCode.Z_DATA_ERROR if the input is invalid (an over-subscribed set of lengths), or (int)ZLibResultCode.Z_DATA_ERROR if not enough memory. + /// Return (int)ZLibResultCode.Z_OK on success, (int)ZLibResultCode.Z_BUF_ERROR if the given code set is incomplete (the tables are still built in this case), (int)ZLibResultCode.Z_DATA_ERROR if the input is invalid (an over-subscribed set of lengths), or (int)ZLibResultCode.Z_MEM_ERROR if not enough memory. /// private static int huft_build(int[] b, int bindex, int n, int s, int[] d, int[] e, int[] t, int[] m, int[] hp, int[] hn, int[] v) { @@ -293,7 +293,7 @@ private static int huft_build(int[] b, int bindex, int n, int s, int[] d, int[] } } } - // Return (int)ZLibResultCode.Z_DATA_ERROR if we were given an incomplete table + // Return (int)ZLibResultCode.Z_BUF_ERROR if we were given an incomplete table return y != 0 && g != 1 ? (int)ZLibResultCode.Z_BUF_ERROR : (int)ZLibResultCode.Z_OK; } @@ -337,7 +337,7 @@ internal static int inflate_trees_dynamic(int nl, int nd, int[] c, int[] bl, int { z.msg = "oversubscribed literal/length tree"; } - else if (r != (int)ZLibResultCode.Z_DATA_ERROR) + else if (r != (int)ZLibResultCode.Z_MEM_ERROR) { z.msg = "incomplete literal/length tree"; r = (int)ZLibResultCode.Z_DATA_ERROR; @@ -359,7 +359,7 @@ internal static int inflate_trees_dynamic(int nl, int nd, int[] c, int[] bl, int z.msg = "incomplete distance tree"; r = (int)ZLibResultCode.Z_DATA_ERROR; } - else if (r != (int)ZLibResultCode.Z_DATA_ERROR) + else if (r != (int)ZLibResultCode.Z_MEM_ERROR) { z.msg = "empty distance tree with lengths"; r = (int)ZLibResultCode.Z_DATA_ERROR; diff --git a/src/core/IronPython.Modules/zlib/zlib.net/Inflate.cs b/src/core/IronPython.Modules/zlib/zlib.net/Inflate.cs index 6cf3e0d5b..a32f8f382 100644 --- a/src/core/IronPython.Modules/zlib/zlib.net/Inflate.cs +++ b/src/core/IronPython.Modules/zlib/zlib.net/Inflate.cs @@ -309,7 +309,7 @@ internal int inflate(ZStream z, FlushStrategy flush) if ((z.istate.method >> 4) + 8 > z.istate.wbits) { z.istate.mode = InflateMode.BAD; - z.msg = "invalid Window size"; + z.msg = "invalid window size"; z.istate.marker = 5; // can't try inflateSync break; } From e1c8e278c4bc1ad98fd18698575073e75c2230d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Thu, 5 Nov 2026 18:38:38 -0500 Subject: [PATCH 2/5] Enable test_zlib --- .../IronPython.Modules/zlib/Decompress.cs | 17 ++++---- .../IronPython.Modules/zlib/ZlibModule.cs | 20 +++++---- .../Cases/CPythonCasesManifest.ini | 3 -- tests/suite/stdlib/test_zlib.py | 42 +++++++++++++++++++ 4 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 tests/suite/stdlib/test_zlib.py diff --git a/src/core/IronPython.Modules/zlib/Decompress.cs b/src/core/IronPython.Modules/zlib/Decompress.cs index c0965e6ad..5273b144e 100644 --- a/src/core/IronPython.Modules/zlib/Decompress.cs +++ b/src/core/IronPython.Modules/zlib/Decompress.cs @@ -90,19 +90,20 @@ public Bytes decompress([NotNone] IBufferProtocol data, int max_length=0) err = zst.inflate(FlushStrategy.Z_SYNC_FLUSH); } - if(max_length > 0) - { - unconsumed_tail = GetBytes(zst.next_in, zst.next_in_index, zst.avail_in); - } - + var remaining = GetBytes(zst.next_in, zst.next_in_index, zst.avail_in); if(err == Z_STREAM_END) { - unused_data += GetBytes(zst.next_in, zst.next_in_index, zst.avail_in); + unused_data += remaining; + unconsumed_tail = Bytes.Empty; eof = true; } - else if(err != Z_OK && err != Z_BUF_ERROR) + else { - throw ZlibModule.zlib_error(this.zst, err, "while decompressing"); + unconsumed_tail = remaining; + if(err != Z_OK && err != Z_BUF_ERROR) + { + throw ZlibModule.zlib_error(this.zst, err, "while decompressing"); + } } return GetBytes(output, 0, (int)(zst.total_out - start_total_out)); diff --git a/src/core/IronPython.Modules/zlib/ZlibModule.cs b/src/core/IronPython.Modules/zlib/ZlibModule.cs index 208c61417..b88a1bd9f 100644 --- a/src/core/IronPython.Modules/zlib/ZlibModule.cs +++ b/src/core/IronPython.Modules/zlib/ZlibModule.cs @@ -39,6 +39,7 @@ public static class ZlibModule objects support decompress() and flush()."; public const string ZLIB_VERSION = "1.2.3"; // just match the zlib version in Python 2.6 + public const string ZLIB_RUNTIME_VERSION = ZLIB_VERSION; internal const int Z_OK = (int)ZLibResultCode.Z_OK; internal const int Z_STREAM_END = (int)ZLibResultCode.Z_STREAM_END; @@ -68,21 +69,22 @@ public static class ZlibModule public const int MAX_WBITS = ZLibUtil.MAX_WBITS; internal const int DEFAULTALLOC = 16 * 1024; + public const int DEF_BUF_SIZE = DEFAULTALLOC; [Documentation(@"adler32(data[, value]) -- Compute an Adler-32 checksum of data. -An optional starting value can be specified. The returned checksum is -a signed integer.")] - public static int adler32([NotNone] IBufferProtocol data, long value=1L) +An optional starting value can be specified. The returned checksum is an integer.")] + public static object adler32([NotNone] IBufferProtocol data, long value=1L) { using var buffer = data.GetBuffer(); - return (int)Adler32.GetAdler32Checksum(value, buffer.AsUnsafeArray() ?? buffer.ToArray(), 0, buffer.NumBytes()); + var res = (uint)Adler32.GetAdler32Checksum(value, buffer.AsUnsafeArray() ?? buffer.ToArray(), 0, buffer.NumBytes()); + if (res <= int.MaxValue) return (int)res; + return (BigInteger)res; } [Documentation(@"crc32(data[, value]) -- Compute a CRC-32 checksum of data. -An optional starting value can be specified. The returned checksum is -a signed integer.")] +An optional starting value can be specified. The returned checksum is an integer.")] public static object crc32([NotNone] IBufferProtocol data, uint value = 0) // TODO: [PythonIndex(overflow=mask)] uint value = 0 => IronPython.Modules.PythonBinaryAscii.crc32(data, value); @@ -108,11 +110,11 @@ public static Bytes compress([NotNone] IBufferProtocol data, { case (Z_OK): break; - + case (Z_STREAM_ERROR): throw PythonOps.CreateThrowable(error, "Bad compression level"); - + default: zst.deflateEnd(); zlib_error(zst, err, "while compressing data"); @@ -211,7 +213,7 @@ internal static Exception zlib_error(ZStream zst, int err, string msg) } [PythonHidden] - internal static byte[] Decompress(byte[] input, int wbits=MAX_WBITS, int bufsize=DEFAULTALLOC) + internal static byte[] Decompress(byte[] input, int wbits=MAX_WBITS, int bufsize=DEFAULTALLOC) { byte[] outputBuffer = new byte[bufsize]; byte[] output = new byte[bufsize]; diff --git a/tests/IronPython.Tests/Cases/CPythonCasesManifest.ini b/tests/IronPython.Tests/Cases/CPythonCasesManifest.ini index 4e02118b8..192475484 100644 --- a/tests/IronPython.Tests/Cases/CPythonCasesManifest.ini +++ b/tests/IronPython.Tests/Cases/CPythonCasesManifest.ini @@ -1100,9 +1100,6 @@ Reason=ValueError: unmarshallable object [CPython.test_zipimport_support] Ignore=true -[CPython.test_zlib] -Ignore=true - [CPython.unittest.test_break] Ignore=true diff --git a/tests/suite/stdlib/test_zlib.py b/tests/suite/stdlib/test_zlib.py new file mode 100644 index 000000000..9ac86f1e5 --- /dev/null +++ b/tests/suite/stdlib/test_zlib.py @@ -0,0 +1,42 @@ +# Licensed to the .NET Foundation under one or more agreements. +# The .NET Foundation licenses this file to you under the Apache 2.0 License. +# See the LICENSE file in the project root for more information. + +## +## Run selected tests from test_zlib from StdLib +## + +import sys + +from iptest import is_ironpython, generate_suite, run_test + +import test.test_zlib + +def load_tests(loader, standard_tests, pattern): + tests = loader.loadTestsFromModule(test.test_zlib, pattern=pattern) + + if is_ironpython: + failing_tests = [ + test.test_zlib.CompressObjectTestCase('test_dictionary'), # compressobj() has no zdict argument + test.test_zlib.CompressObjectTestCase('test_dictionary_streaming'), # compressobj() has no zdict argument + ] + if sys.version_info >= (3, 6): + failing_tests += [ + test.test_zlib.CompressObjectTestCase('test_decompress_raw_with_dictionary'), # compressobj() has no zdict argument + test.test_zlib.CompressObjectTestCase('test_keywords'), # compressobj() has no zdict argument + test.test_zlib.CompressObjectTestCase('test_wbits'), # compressobj() can't produce gzip output + test.test_zlib.CompressTestCase('test_keywords'), # compress() data argument isn't positional-only + ] + else: + failing_tests += [ + test.test_zlib.CompressObjectTestCase('test_compressoptions'), # compressobj() has no zdict argument + ] + + skip_tests = [] + + return generate_suite(tests, failing_tests, skip_tests) + + else: + return tests + +run_test(__name__) From 56c3986242aec14fa8c48323ef1212f890be6090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Thu, 5 Nov 2026 21:21:44 -0500 Subject: [PATCH 3/5] Apply default formatting to zlib --- src/core/IronPython.Modules/zlib/Compress.cs | 48 ++++------ .../IronPython.Modules/zlib/Decompress.cs | 52 ++++------- .../IronPython.Modules/zlib/ZlibModule.cs | 92 +++++++------------ .../zlib/zlib.net/.editorconfig | 9 ++ 4 files changed, 82 insertions(+), 119 deletions(-) create mode 100644 src/core/IronPython.Modules/zlib/zlib.net/.editorconfig diff --git a/src/core/IronPython.Modules/zlib/Compress.cs b/src/core/IronPython.Modules/zlib/Compress.cs index 4f07b7fdf..73ad5b4c6 100644 --- a/src/core/IronPython.Modules/zlib/Compress.cs +++ b/src/core/IronPython.Modules/zlib/Compress.cs @@ -9,30 +9,29 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; + using ComponentAce.Compression.Libs.ZLib; + using IronPython.Runtime; using IronPython.Runtime.Operations; + using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; -namespace IronPython.Zlib -{ +namespace IronPython.Zlib { [PythonType] - public class Compress - { + public class Compress { private const int Z_OK = ZlibModule.Z_OK; private const int Z_BUF_ERROR = ZlibModule.Z_BUF_ERROR; private const int Z_STREAM_END = ZlibModule.Z_STREAM_END; - + private const int Z_NO_FLUSH = ZlibModule.Z_NO_FLUSH; private const int Z_FINISH = ZlibModule.Z_FINISH; - internal Compress(int level, int method, int wbits, int memlevel, int strategy) - { + internal Compress(int level, int method, int wbits, int memlevel, int strategy) { zst = new ZStream(); int err = zst.DeflateInit(level, wbits); - switch(err) - { + switch (err) { case ZlibModule.Z_OK: break; @@ -49,8 +48,7 @@ internal Compress(int level, int method, int wbits, int memlevel, int strategy) After calling this function, some of the input data may still be stored in internal buffers for later processing. Call the flush() method to clear these buffers.")] - public Bytes compress([NotNone] IBufferProtocol data) - { + public Bytes compress([NotNone] IBufferProtocol data) { using var buffer = data.GetBuffer(); byte[] input = buffer.AsUnsafeArray() ?? buffer.ToArray(); byte[] output = new byte[ZlibModule.DEFAULTALLOC]; @@ -65,8 +63,7 @@ public Bytes compress([NotNone] IBufferProtocol data) int err = zst.deflate(Z_NO_FLUSH); - while(err == Z_OK && zst.avail_out == 0) - { + while (err == Z_OK && zst.avail_out == 0) { int length = output.Length; Array.Resize(ref output, output.Length * 2); @@ -76,8 +73,7 @@ public Bytes compress([NotNone] IBufferProtocol data) err = zst.deflate(Z_NO_FLUSH); } - if(err != Z_OK && err != Z_BUF_ERROR) - { + if (err != Z_OK && err != Z_BUF_ERROR) { throw ZlibModule.zlib_error(this.zst, err, "while compressing"); } @@ -90,12 +86,10 @@ public Bytes compress([NotNone] IBufferProtocol data) default value used when mode is not specified is Z_FINISH. If mode == Z_FINISH, the compressor object can no longer be used after calling the flush() method. Otherwise, more data can still be compressed.")] - public Bytes flush(int mode=Z_FINISH) - { + public Bytes flush(int mode = Z_FINISH) { byte[] output = new byte[ZlibModule.DEFAULTALLOC]; - if(mode == Z_NO_FLUSH) - { + if (mode == Z_NO_FLUSH) { return Bytes.Empty; } @@ -106,8 +100,7 @@ public Bytes flush(int mode=Z_FINISH) zst.avail_out = output.Length; int err = zst.deflate((FlushStrategy)mode); - while(err == Z_OK && zst.avail_out == 0) - { + while (err == Z_OK && zst.avail_out == 0) { int old_length = output.Length; Array.Resize(ref output, output.Length * 2); @@ -117,16 +110,12 @@ public Bytes flush(int mode=Z_FINISH) err = zst.deflate((FlushStrategy)mode); } - if(err == Z_STREAM_END && mode == Z_FINISH) - { + if (err == Z_STREAM_END && mode == Z_FINISH) { err = zst.deflateEnd(); - if(err != Z_OK) - { + if (err != Z_OK) { throw ZlibModule.zlib_error(this.zst, err, "from deflateEnd()"); } - } - else if(err != Z_OK && err != Z_BUF_ERROR) - { + } else if (err != Z_OK && err != Z_BUF_ERROR) { throw ZlibModule.zlib_error(this.zst, err, "while flushing"); } @@ -141,8 +130,7 @@ public Bytes flush(int mode=Z_FINISH) private ZStream zst; - private static Bytes GetBytes(byte[] bytes, int index, int count) - { + private static Bytes GetBytes(byte[] bytes, int index, int count) { var res = new byte[count]; Array.Copy(bytes, index, res, 0, count); return Bytes.Make(res); diff --git a/src/core/IronPython.Modules/zlib/Decompress.cs b/src/core/IronPython.Modules/zlib/Decompress.cs index 5273b144e..1f3663d71 100644 --- a/src/core/IronPython.Modules/zlib/Decompress.cs +++ b/src/core/IronPython.Modules/zlib/Decompress.cs @@ -9,17 +9,18 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; + using ComponentAce.Compression.Libs.ZLib; + using IronPython.Runtime; using IronPython.Runtime.Operations; + using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; -namespace IronPython.Zlib -{ +namespace IronPython.Zlib { [PythonType] - public class Decompress - { + public class Decompress { private const int Z_OK = ZlibModule.Z_OK; private const int Z_STREAM_END = ZlibModule.Z_STREAM_END; private const int Z_BUF_ERROR = ZlibModule.Z_BUF_ERROR; @@ -27,12 +28,10 @@ public class Decompress private const int Z_SYNC_FLUSH = ZlibModule.Z_SYNC_FLUSH; private const int Z_FINISH = ZlibModule.Z_FINISH; - internal Decompress(int wbits) - { + internal Decompress(int wbits) { zst = new ZStream(); int err = zst.inflateInit(wbits); - switch(err) - { + switch (err) { case ZlibModule.Z_OK: break; @@ -59,9 +58,8 @@ Call the flush() method to clear these buffers. If the max_length parameter is specified then the return value will be no longer than max_length. Unconsumed input data will be stored in the unconsumed_tail attribute.")] - public Bytes decompress([NotNone] IBufferProtocol data, int max_length=0) - { - if(max_length < 0) throw new ArgumentException("max_length must be greater than zero"); + public Bytes decompress([NotNone] IBufferProtocol data, int max_length = 0) { + if (max_length < 0) throw new ArgumentException("max_length must be greater than zero"); using var buffer = data.GetBuffer(); byte[] input = buffer.AsUnsafeArray() ?? buffer.ToArray(); @@ -77,9 +75,8 @@ public Bytes decompress([NotNone] IBufferProtocol data, int max_length=0) int err = zst.inflate(FlushStrategy.Z_SYNC_FLUSH); - while(err == Z_OK && zst.avail_out == 0) - { - if(max_length > 0 && output.Length >= max_length) + while (err == Z_OK && zst.avail_out == 0) { + if (max_length > 0 && output.Length >= max_length) break; int old_length = output.Length; @@ -91,17 +88,13 @@ public Bytes decompress([NotNone] IBufferProtocol data, int max_length=0) } var remaining = GetBytes(zst.next_in, zst.next_in_index, zst.avail_in); - if(err == Z_STREAM_END) - { + if (err == Z_STREAM_END) { unused_data += remaining; unconsumed_tail = Bytes.Empty; eof = true; - } - else - { + } else { unconsumed_tail = remaining; - if(err != Z_OK && err != Z_BUF_ERROR) - { + if (err != Z_OK && err != Z_BUF_ERROR) { throw ZlibModule.zlib_error(this.zst, err, "while decompressing"); } } @@ -116,9 +109,8 @@ public Bytes decompress([NotNone] IBufferProtocol data, int max_length=0) output buffer. The decompressor object can no longer be used after this call.")] - public Bytes flush(int length=ZlibModule.DEFAULTALLOC) - { - if(length < 1) + public Bytes flush(int length = ZlibModule.DEFAULTALLOC) { + if (length < 1) throw PythonOps.ValueError("length must be greater than 0."); byte[] output = new byte[length]; @@ -130,8 +122,7 @@ public Bytes flush(int length=ZlibModule.DEFAULTALLOC) int err = zst.inflate(FlushStrategy.Z_FINISH); - while((err == Z_OK || err == Z_BUF_ERROR) &&zst.avail_out == 0) - { + while ((err == Z_OK || err == Z_BUF_ERROR) && zst.avail_out == 0) { int old_length = output.Length; Array.Resize(ref output, output.Length * 2); zst.next_out = output; @@ -140,11 +131,9 @@ public Bytes flush(int length=ZlibModule.DEFAULTALLOC) err = zst.inflate(FlushStrategy.Z_FINISH); } - if(err == Z_STREAM_END) - { + if (err == Z_STREAM_END) { err = zst.inflateEnd(); - if(err != Z_OK) - { + if (err != Z_OK) { throw ZlibModule.zlib_error(this.zst, err, "from inflateEnd()"); } } @@ -160,8 +149,7 @@ public Bytes flush(int length=ZlibModule.DEFAULTALLOC) private ZStream zst; - private static Bytes GetBytes(byte[] bytes, int index, int count) - { + private static Bytes GetBytes(byte[] bytes, int index, int count) { var res = new byte[count]; Array.Copy(bytes, index, res, 0, count); return Bytes.Make(res); diff --git a/src/core/IronPython.Modules/zlib/ZlibModule.cs b/src/core/IronPython.Modules/zlib/ZlibModule.cs index b88a1bd9f..da47ee02c 100644 --- a/src/core/IronPython.Modules/zlib/ZlibModule.cs +++ b/src/core/IronPython.Modules/zlib/ZlibModule.cs @@ -10,20 +10,21 @@ using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; + using ComponentAce.Compression.Libs.ZLib; + using IronPython.Runtime; using IronPython.Runtime.Exceptions; using IronPython.Runtime.Operations; using IronPython.Runtime.Types; + using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; [assembly: PythonModule("zlib", typeof(IronPython.Zlib.ZlibModule))] -namespace IronPython.Zlib -{ - public static class ZlibModule - { +namespace IronPython.Zlib { + public static class ZlibModule { public const string __doc__ = @"The functions in this module allow compression and decompression using the zlib library, which is based on GNU zip. @@ -74,8 +75,7 @@ public static class ZlibModule [Documentation(@"adler32(data[, value]) -- Compute an Adler-32 checksum of data. An optional starting value can be specified. The returned checksum is an integer.")] - public static object adler32([NotNone] IBufferProtocol data, long value=1L) - { + public static object adler32([NotNone] IBufferProtocol data, long value = 1L) { using var buffer = data.GetBuffer(); var res = (uint)Adler32.GetAdler32Checksum(value, buffer.AsUnsafeArray() ?? buffer.ToArray(), 0, buffer.NumBytes()); if (res <= int.MaxValue) return (int)res; @@ -93,8 +93,7 @@ public static object crc32([NotNone] IBufferProtocol data, uint value = 0) Optional arg level is the compression level, in 1-9.")] public static Bytes compress([NotNone] IBufferProtocol data, - int level=Z_DEFAULT_COMPRESSION) - { + int level = Z_DEFAULT_COMPRESSION) { using var buffer = data.GetBuffer(); byte[] input = buffer.AsUnsafeArray() ?? buffer.ToArray(); byte[] output = new byte[input.Length + input.Length / 1000 + 12 + 1]; @@ -106,8 +105,7 @@ public static Bytes compress([NotNone] IBufferProtocol data, zst.avail_out = output.Length; int err = zst.DeflateInit(level); - switch(err) - { + switch (err) { case (Z_OK): break; @@ -123,16 +121,14 @@ public static Bytes compress([NotNone] IBufferProtocol data, err = zst.deflate(FlushStrategy.Z_FINISH); - if(err != Z_STREAM_END) - { + if (err != Z_STREAM_END) { zst.deflateEnd(); throw zlib_error(zst, err, "while compressing data"); } err = zst.deflateEnd(); - if(err == Z_OK) - { + if (err == Z_OK) { var res = new byte[(int)zst.total_out]; Array.Copy(output, res, res.Length); return Bytes.Make(res); @@ -145,12 +141,11 @@ public static Bytes compress([NotNone] IBufferProtocol data, Optional arg level is the compression level, in 1-9.")] public static Compress compressobj( - int level=Z_DEFAULT_COMPRESSION, - int method=DEFLATED, - int wbits=MAX_WBITS, - int memlevel=DEF_MEM_LEVEL, - int strategy=Z_DEFAULT_STRATEGY) - { + int level = Z_DEFAULT_COMPRESSION, + int method = DEFLATED, + int wbits = MAX_WBITS, + int memlevel = DEF_MEM_LEVEL, + int strategy = Z_DEFAULT_STRATEGY) { return new Compress(level, method, wbits, memlevel, strategy); } @@ -159,9 +154,8 @@ public static Compress compressobj( Optional arg wbits is the window buffer size. Optional arg bufsize is the initial output buffer size.")] public static Bytes decompress([NotNone] IBufferProtocol data, - int wbits=MAX_WBITS, - int bufsize=DEFAULTALLOC) - { + int wbits = MAX_WBITS, + int bufsize = DEFAULTALLOC) { using var buffer = data.GetBuffer(); var bytes = Decompress(buffer.AsUnsafeArray() ?? buffer.ToArray(), wbits, bufsize); return Bytes.Make(bytes); @@ -170,30 +164,24 @@ public static Bytes decompress([NotNone] IBufferProtocol data, [Documentation(@"decompressobj([wbits]) -- Return a decompressor object. Optional arg wbits is the window buffer size.")] - public static Decompress decompressobj(int wbits=MAX_WBITS) - { + public static Decompress decompressobj(int wbits = MAX_WBITS) { return new Decompress(wbits); } [SpecialName] - public static void PerformModuleReload(PythonContext context, PythonDictionary dict) - { + public static void PerformModuleReload(PythonContext context, PythonDictionary dict) { error = context.EnsureModuleException("zlib.error", PythonExceptions.Exception, dict, "error", "zlib"); } public static PythonType error; - internal static Exception MakeError(params object[] args) - { + internal static Exception MakeError(params object[] args) { return PythonOps.CreateThrowable(error, args); } - internal static Exception zlib_error(ZStream zst, int err, string msg) - { + internal static Exception zlib_error(ZStream zst, int err, string msg) { string zmsg = zst.msg; - if(zmsg == null) - { - switch(err) - { + if (zmsg == null) { + switch (err) { case Z_BUF_ERROR: zmsg = "incomplete or truncated stream"; break; @@ -206,15 +194,14 @@ internal static Exception zlib_error(ZStream zst, int err, string msg) } } - if(zmsg == null) + if (zmsg == null) return MakeError(string.Format("Error {0} {1}", err, msg)); else return MakeError(string.Format("Error {0} {1}: {2}", err, msg, zmsg)); } [PythonHidden] - internal static byte[] Decompress(byte[] input, int wbits=MAX_WBITS, int bufsize=DEFAULTALLOC) - { + internal static byte[] Decompress(byte[] input, int wbits = MAX_WBITS, int bufsize = DEFAULTALLOC) { byte[] outputBuffer = new byte[bufsize]; byte[] output = new byte[bufsize]; int outputOffset = 0; @@ -226,26 +213,20 @@ internal static byte[] Decompress(byte[] input, int wbits=MAX_WBITS, int bufsize zst.avail_out = outputBuffer.Length; int err = zst.inflateInit(wbits); - if(err != Z_OK) - { + if (err != Z_OK) { zst.inflateEnd(); throw zlib_error(zst, err, "while preparing to decompress data"); } - do - { + do { err = zst.inflate(FlushStrategy.Z_FINISH); - if(err != Z_STREAM_END) - { - if(err == Z_BUF_ERROR && zst.avail_out > 0) - { + if (err != Z_STREAM_END) { + if (err == Z_BUF_ERROR && zst.avail_out > 0) { zst.inflateEnd(); throw zlib_error(zst, err, "while decompressing data"); - } - else if(err == Z_OK || (err == Z_BUF_ERROR && zst.avail_out == 0)) - { + } else if (err == Z_OK || (err == Z_BUF_ERROR && zst.avail_out == 0)) { // copy to the output and reset the buffer - if(outputOffset + outputBuffer.Length > output.Length) + if (outputOffset + outputBuffer.Length > output.Length) Array.Resize(ref output, output.Length * 2); Array.Copy(outputBuffer, 0, output, outputOffset, outputBuffer.Length); @@ -254,23 +235,20 @@ internal static byte[] Decompress(byte[] input, int wbits=MAX_WBITS, int bufsize zst.next_out = outputBuffer; zst.avail_out = outputBuffer.Length; zst.next_out_index = 0; - } - else - { + } else { zst.inflateEnd(); throw zlib_error(zst, err, "while decompressing data"); } } - } while(err != Z_STREAM_END); + } while (err != Z_STREAM_END); err = zst.inflateEnd(); - if(err != Z_OK) - { + if (err != Z_OK) { throw zlib_error(zst, err, "while finishing data decompression"); } - if(outputOffset + outputBuffer.Length - zst.avail_out > output.Length) + if (outputOffset + outputBuffer.Length - zst.avail_out > output.Length) Array.Resize(ref output, output.Length * 2); Array.Copy(outputBuffer, 0, output, outputOffset, outputBuffer.Length - zst.avail_out); diff --git a/src/core/IronPython.Modules/zlib/zlib.net/.editorconfig b/src/core/IronPython.Modules/zlib/zlib.net/.editorconfig new file mode 100644 index 000000000..7ec4aff45 --- /dev/null +++ b/src/core/IronPython.Modules/zlib/zlib.net/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +trim_trailing_whitespace = false +insert_final_newline = false + +[*.cs] +# leave upstream code formatting and style intact +generated_code = true From 258da5fd965630d9eac9c1e9d83bb71b8915d4d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Thu, 5 Nov 2026 21:51:45 -0500 Subject: [PATCH 4/5] Fix exception context --- src/core/IronPython.Modules/zipimport.cs | 2 +- src/core/IronPython.Modules/zlib/Compress.cs | 26 ++++----- .../IronPython.Modules/zlib/Decompress.cs | 24 ++++---- .../IronPython.Modules/zlib/ZlibModule.cs | 55 ++++++++++--------- 4 files changed, 51 insertions(+), 56 deletions(-) diff --git a/src/core/IronPython.Modules/zipimport.cs b/src/core/IronPython.Modules/zipimport.cs index 072d490b6..5c4c1419c 100644 --- a/src/core/IronPython.Modules/zipimport.cs +++ b/src/core/IronPython.Modules/zipimport.cs @@ -427,7 +427,7 @@ private byte[] GetData(CodeContext context, string archive, PythonTuple toc_entr data = raw_data; } else { // decompress with zlib - data = ZlibModule.Decompress(raw_data, -15); + data = ZlibModule.Decompress(context, raw_data, -15); } } finally { diff --git a/src/core/IronPython.Modules/zlib/Compress.cs b/src/core/IronPython.Modules/zlib/Compress.cs index 73ad5b4c6..f33c0da4f 100644 --- a/src/core/IronPython.Modules/zlib/Compress.cs +++ b/src/core/IronPython.Modules/zlib/Compress.cs @@ -6,9 +6,7 @@ // using System; -using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; using ComponentAce.Compression.Libs.ZLib; @@ -16,7 +14,6 @@ using IronPython.Runtime.Operations; using Microsoft.Scripting.Runtime; -using Microsoft.Scripting.Utils; namespace IronPython.Zlib { [PythonType] @@ -28,7 +25,11 @@ public class Compress { private const int Z_NO_FLUSH = ZlibModule.Z_NO_FLUSH; private const int Z_FINISH = ZlibModule.Z_FINISH; - internal Compress(int level, int method, int wbits, int memlevel, int strategy) { + private readonly CodeContext _context; + private ZStream zst; + + internal Compress(CodeContext context, int level, int method, int wbits, int memlevel, int strategy) { + _context = context; zst = new ZStream(); int err = zst.DeflateInit(level, wbits); switch (err) { @@ -39,7 +40,7 @@ internal Compress(int level, int method, int wbits, int memlevel, int strategy) throw PythonOps.ValueError("Invalid initialization option"); default: - throw ZlibModule.zlib_error(this.zst, err, "while creating compression object"); + throw ZlibModule.zlib_error(_context, this.zst, err, "while creating compression object"); } } @@ -74,7 +75,7 @@ public Bytes compress([NotNone] IBufferProtocol data) { } if (err != Z_OK && err != Z_BUF_ERROR) { - throw ZlibModule.zlib_error(this.zst, err, "while compressing"); + throw ZlibModule.zlib_error(_context, this.zst, err, "while compressing"); } return GetBytes(output, 0, (int)(zst.total_out - start_total_out)); @@ -113,22 +114,17 @@ public Bytes flush(int mode = Z_FINISH) { if (err == Z_STREAM_END && mode == Z_FINISH) { err = zst.deflateEnd(); if (err != Z_OK) { - throw ZlibModule.zlib_error(this.zst, err, "from deflateEnd()"); + throw ZlibModule.zlib_error(_context, this.zst, err, "from deflateEnd()"); } } else if (err != Z_OK && err != Z_BUF_ERROR) { - throw ZlibModule.zlib_error(this.zst, err, "while flushing"); + throw ZlibModule.zlib_error(_context, this.zst, err, "while flushing"); } return GetBytes(output, 0, (int)(zst.total_out - start_total_out)); } - //[Documentation("copy() -- Return a copy of the compression object.")] - //public Compress copy() - //{ - // throw new NotImplementedException(); - //} - - private ZStream zst; + // TODO: needs a deep clone of ZStream (zlib.NET has no deflateCopy/inflateCopy) + //public Compress copy() => throw new NotImplementedException(); private static Bytes GetBytes(byte[] bytes, int index, int count) { var res = new byte[count]; diff --git a/src/core/IronPython.Modules/zlib/Decompress.cs b/src/core/IronPython.Modules/zlib/Decompress.cs index 1f3663d71..c3d48c820 100644 --- a/src/core/IronPython.Modules/zlib/Decompress.cs +++ b/src/core/IronPython.Modules/zlib/Decompress.cs @@ -6,9 +6,7 @@ // using System; -using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; using ComponentAce.Compression.Libs.ZLib; @@ -16,7 +14,6 @@ using IronPython.Runtime.Operations; using Microsoft.Scripting.Runtime; -using Microsoft.Scripting.Utils; namespace IronPython.Zlib { [PythonType] @@ -28,7 +25,11 @@ public class Decompress { private const int Z_SYNC_FLUSH = ZlibModule.Z_SYNC_FLUSH; private const int Z_FINISH = ZlibModule.Z_FINISH; - internal Decompress(int wbits) { + private readonly CodeContext _context; + private ZStream zst; + + internal Decompress(CodeContext context, int wbits) { + _context = context; zst = new ZStream(); int err = zst.inflateInit(wbits); switch (err) { @@ -39,7 +40,7 @@ internal Decompress(int wbits) { throw PythonOps.ValueError("Invalid initialization option"); default: - throw ZlibModule.zlib_error(this.zst, err, "while creating decompression object"); + throw ZlibModule.zlib_error(_context, this.zst, err, "while creating decompression object"); } unused_data = Bytes.Empty; @@ -95,7 +96,7 @@ public Bytes decompress([NotNone] IBufferProtocol data, int max_length = 0) { } else { unconsumed_tail = remaining; if (err != Z_OK && err != Z_BUF_ERROR) { - throw ZlibModule.zlib_error(this.zst, err, "while decompressing"); + throw ZlibModule.zlib_error(_context, this.zst, err, "while decompressing"); } } @@ -134,20 +135,15 @@ public Bytes flush(int length = ZlibModule.DEFAULTALLOC) { if (err == Z_STREAM_END) { err = zst.inflateEnd(); if (err != Z_OK) { - throw ZlibModule.zlib_error(this.zst, err, "from inflateEnd()"); + throw ZlibModule.zlib_error(_context, this.zst, err, "from inflateEnd()"); } } return GetBytes(output, 0, (int)(zst.total_out - start_total_out)); } - //[Documentation("copy() -- Return a copy of the decompression object.")] - //public Compress copy() - //{ - // throw new NotImplementedException(); - //} - - private ZStream zst; + // TODO: needs a deep clone of ZStream (zlib.NET has no deflateCopy/inflateCopy) + //public Decompress copy() => throw new NotImplementedException(); private static Bytes GetBytes(byte[] bytes, int index, int count) { var res = new byte[count]; diff --git a/src/core/IronPython.Modules/zlib/ZlibModule.cs b/src/core/IronPython.Modules/zlib/ZlibModule.cs index da47ee02c..018830273 100644 --- a/src/core/IronPython.Modules/zlib/ZlibModule.cs +++ b/src/core/IronPython.Modules/zlib/ZlibModule.cs @@ -92,7 +92,7 @@ public static object crc32([NotNone] IBufferProtocol data, uint value = 0) [Documentation(@"compress(data[, level]) -- Returns a bytes object containing compressed data. Optional arg level is the compression level, in 1-9.")] - public static Bytes compress([NotNone] IBufferProtocol data, + public static Bytes compress(CodeContext context, [NotNone] IBufferProtocol data, int level = Z_DEFAULT_COMPRESSION) { using var buffer = data.GetBuffer(); byte[] input = buffer.AsUnsafeArray() ?? buffer.ToArray(); @@ -110,20 +110,18 @@ public static Bytes compress([NotNone] IBufferProtocol data, break; case (Z_STREAM_ERROR): - throw PythonOps.CreateThrowable(error, - "Bad compression level"); + throw MakeError(context, "Bad compression level"); default: zst.deflateEnd(); - zlib_error(zst, err, "while compressing data"); - return null; + throw zlib_error(context, zst, err, "while compressing data"); } err = zst.deflate(FlushStrategy.Z_FINISH); if (err != Z_STREAM_END) { zst.deflateEnd(); - throw zlib_error(zst, err, "while compressing data"); + throw zlib_error(context, zst, err, "while compressing data"); } err = zst.deflateEnd(); @@ -134,51 +132,56 @@ public static Bytes compress([NotNone] IBufferProtocol data, return Bytes.Make(res); } - throw zlib_error(zst, err, "while finishing compression"); + throw zlib_error(context, zst, err, "while finishing compression"); } [Documentation(@"compressobj([level]) -- Return a compressor object. Optional arg level is the compression level, in 1-9.")] - public static Compress compressobj( + public static Compress compressobj(CodeContext context, int level = Z_DEFAULT_COMPRESSION, int method = DEFLATED, int wbits = MAX_WBITS, int memlevel = DEF_MEM_LEVEL, int strategy = Z_DEFAULT_STRATEGY) { - return new Compress(level, method, wbits, memlevel, strategy); + return new Compress(context, level, method, wbits, memlevel, strategy); } [Documentation(@"decompress(data[, wbits[, bufsize]]) -- Returns a bytes object containing the uncompressed data. Optional arg wbits is the window buffer size. Optional arg bufsize is the initial output buffer size.")] - public static Bytes decompress([NotNone] IBufferProtocol data, + public static Bytes decompress(CodeContext context, [NotNone] IBufferProtocol data, int wbits = MAX_WBITS, int bufsize = DEFAULTALLOC) { using var buffer = data.GetBuffer(); - var bytes = Decompress(buffer.AsUnsafeArray() ?? buffer.ToArray(), wbits, bufsize); + var bytes = Decompress(context, buffer.AsUnsafeArray() ?? buffer.ToArray(), wbits, bufsize); return Bytes.Make(bytes); } [Documentation(@"decompressobj([wbits]) -- Return a decompressor object. Optional arg wbits is the window buffer size.")] - public static Decompress decompressobj(int wbits = MAX_WBITS) { - return new Decompress(wbits); + public static Decompress decompressobj(CodeContext context, int wbits = MAX_WBITS) { + return new Decompress(context, wbits); } + private static readonly object _errorKey = new object(); + [SpecialName] public static void PerformModuleReload(PythonContext context, PythonDictionary dict) { - error = context.EnsureModuleException("zlib.error", PythonExceptions.Exception, dict, "error", "zlib"); + dict["error"] = GetErrorType(context); } - public static PythonType error; - internal static Exception MakeError(params object[] args) { - return PythonOps.CreateThrowable(error, args); - } + // zipimport raises zlib.error without importing zlib, so PerformModuleReload may not have run yet + private static PythonType GetErrorType(PythonContext context) + => context.GetOrCreateModuleState(_errorKey, + () => PythonExceptions.CreateSubType(context, PythonExceptions.Exception, "error", "zlib", null, PythonType.DefaultMakeException)); + + internal static Exception MakeError(CodeContext context, string message) + => PythonOps.CreateThrowable(GetErrorType(context.LanguageContext), message); - internal static Exception zlib_error(ZStream zst, int err, string msg) { + internal static Exception zlib_error(CodeContext context, ZStream zst, int err, string msg) { string zmsg = zst.msg; if (zmsg == null) { switch (err) { @@ -195,13 +198,13 @@ internal static Exception zlib_error(ZStream zst, int err, string msg) { } if (zmsg == null) - return MakeError(string.Format("Error {0} {1}", err, msg)); + return MakeError(context, string.Format("Error {0} {1}", err, msg)); else - return MakeError(string.Format("Error {0} {1}: {2}", err, msg, zmsg)); + return MakeError(context, string.Format("Error {0} {1}: {2}", err, msg, zmsg)); } [PythonHidden] - internal static byte[] Decompress(byte[] input, int wbits = MAX_WBITS, int bufsize = DEFAULTALLOC) { + internal static byte[] Decompress(CodeContext context, byte[] input, int wbits = MAX_WBITS, int bufsize = DEFAULTALLOC) { byte[] outputBuffer = new byte[bufsize]; byte[] output = new byte[bufsize]; int outputOffset = 0; @@ -215,7 +218,7 @@ internal static byte[] Decompress(byte[] input, int wbits = MAX_WBITS, int bufsi int err = zst.inflateInit(wbits); if (err != Z_OK) { zst.inflateEnd(); - throw zlib_error(zst, err, "while preparing to decompress data"); + throw zlib_error(context, zst, err, "while preparing to decompress data"); } do { @@ -223,7 +226,7 @@ internal static byte[] Decompress(byte[] input, int wbits = MAX_WBITS, int bufsi if (err != Z_STREAM_END) { if (err == Z_BUF_ERROR && zst.avail_out > 0) { zst.inflateEnd(); - throw zlib_error(zst, err, "while decompressing data"); + throw zlib_error(context, zst, err, "while decompressing data"); } else if (err == Z_OK || (err == Z_BUF_ERROR && zst.avail_out == 0)) { // copy to the output and reset the buffer if (outputOffset + outputBuffer.Length > output.Length) @@ -237,7 +240,7 @@ internal static byte[] Decompress(byte[] input, int wbits = MAX_WBITS, int bufsi zst.next_out_index = 0; } else { zst.inflateEnd(); - throw zlib_error(zst, err, "while decompressing data"); + throw zlib_error(context, zst, err, "while decompressing data"); } } @@ -245,7 +248,7 @@ internal static byte[] Decompress(byte[] input, int wbits = MAX_WBITS, int bufsi err = zst.inflateEnd(); if (err != Z_OK) { - throw zlib_error(zst, err, "while finishing data decompression"); + throw zlib_error(context, zst, err, "while finishing data decompression"); } if (outputOffset + outputBuffer.Length - zst.avail_out > output.Length) From d0ccb0fb800ba0d4b583b19885885c498dc5a707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Fri, 6 Nov 2026 21:32:30 -0500 Subject: [PATCH 5/5] Don't grow output past max_length --- src/core/IronPython.Modules/zlib/Decompress.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/IronPython.Modules/zlib/Decompress.cs b/src/core/IronPython.Modules/zlib/Decompress.cs index c3d48c820..ffebbaf53 100644 --- a/src/core/IronPython.Modules/zlib/Decompress.cs +++ b/src/core/IronPython.Modules/zlib/Decompress.cs @@ -81,9 +81,11 @@ public Bytes decompress([NotNone] IBufferProtocol data, int max_length = 0) { break; int old_length = output.Length; - Array.Resize(ref output, output.Length * 2); + int new_length = output.Length * 2; + if (max_length > 0 && new_length > max_length) new_length = max_length; + Array.Resize(ref output, new_length); + zst.avail_out = new_length - old_length; zst.next_out = output; - zst.avail_out = old_length; err = zst.inflate(FlushStrategy.Z_SYNC_FLUSH); }