diff --git a/src/libraries/Common/tests/System/IO/Compression/EncoderDecoderTestBase.cs b/src/libraries/Common/tests/System/IO/Compression/EncoderDecoderTestBase.cs
index 9492c2bb185b16..af66929496225b 100644
--- a/src/libraries/Common/tests/System/IO/Compression/EncoderDecoderTestBase.cs
+++ b/src/libraries/Common/tests/System/IO/Compression/EncoderDecoderTestBase.cs
@@ -776,7 +776,6 @@ public void RoundTrip_AllCompressionLevels()
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/127563", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst | TestPlatforms.Android)]
public void RoundTrip_AllWindowLogs()
{
byte[] input = CreateTestData();
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateEncoder.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateEncoder.cs
index f90a62291459f7..c1a96a48a88aab 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateEncoder.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateEncoder.cs
@@ -144,30 +144,16 @@ public static long GetMaxCompressedLength(long inputLength)
{
ArgumentOutOfRangeException.ThrowIfNegative(inputLength);
- // For inputs up to 2 GiB, delegate to the native compressBound() function, which returns
- // the exact upper bound for the zlib implementation linked into the current process
- // (either classic zlib or zlib-ng, depending on platform and build flags). The 2^31
- // threshold keeps the value within the uint P/Invoke signature on all platforms.
-
- // Browser/WASI builds do not link the native compression library,
- // so fall through to the managed formula on those platforms.
- if (inputLength <= (1L << 31) && !OperatingSystem.IsBrowser() && !OperatingSystem.IsWasi())
- {
- return Interop.ZLib.compressBound((uint)inputLength);
- }
-
- // For larger inputs, compute the bound in managed code using zlib-ng's quick-strategy
- // formula. It is strictly larger than classic zlib's compressBound(), so it is a safe
- // upper bound regardless of which implementation is linked at runtime.
- // See: src/native/external/zlib-ng/compress.c and zutil.h.
- // Use ulong to avoid overflow; reject inputs whose bound does not fit in long.
+ // This method does not know which windowLog2 or quality the caller will use, so
+ // compute the maximum of the bounds used by zlib-ng's deflateBound(). The code below mimics the logic
+ // to compute the maximum possible size returned by deflateBound. Our API doesn't expose all knobs
+ // so z_stream's strstart and gzhead are always null. We also don't handle the s390 corner case.
ulong sourceLength = (ulong)inputLength;
- ulong maxCompressedLength = sourceLength
- + (sourceLength == 0 ? 1u : 0u)
- + (sourceLength < 9 ? 1u : 0u)
- + ((sourceLength + 7) >> 3)
- + 3 // DEFLATE_BLOCK_OVERHEAD: (3 + 15 + 6) >> 3
- + 6; // ZLIB_WRAPLEN: zlib header (2 bytes) + Adler32 trailer (4 bytes)
+ const ulong wrapLength = 6; // GZIP_WRAPLEN is 18, GZipEncoder compensates for the rest
+ ulong maxCompressedLength = sourceLength + ((sourceLength + 7) >> 3) + ((sourceLength + 63) >> 6) + 5;
+ ulong storedBlockBound = sourceLength + (sourceLength >> 5) + (sourceLength >> 7) + (sourceLength >> 11) + 7;
+
+ maxCompressedLength = Math.Max(maxCompressedLength, storedBlockBound) + wrapLength;
if (maxCompressedLength > long.MaxValue)
{
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/GZipEncoder.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/GZipEncoder.cs
index 8c5442bf497f76..32aa377da137c6 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/GZipEncoder.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/GZipEncoder.cs
@@ -78,10 +78,9 @@ private void EnsureNotDisposed()
/// is negative.
public static long GetMaxCompressedLength(long inputLength)
{
- // compressBound() returns the upper bound for zlib-wrapped deflate, which includes
- // 6 bytes of zlib overhead (2-byte header + 4-byte Adler32 trailer).
- // GZip format uses 18 bytes of overhead (10-byte header + 8-byte CRC32/size trailer),
- // which is 12 bytes more than the zlib overhead already included in compressBound().
+ // DeflateEncoder.GetMaxCompressedLength() returns the bound for zlib-wrapped deflate,
+ // where wrap length is at most 6 bytes. GZip format uses 18 bytes of overhead, which is
+ // 12 bytes more than the conservative zlib overhead already included in that bound.
long maxCompressedLength = DeflateEncoder.GetMaxCompressedLength(inputLength);
if (maxCompressedLength > long.MaxValue - 12)
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZLibEncoder.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZLibEncoder.cs
index 0749efc0b55ffa..891d49510ae2e7 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZLibEncoder.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZLibEncoder.cs
@@ -78,9 +78,9 @@ private void EnsureNotDisposed()
/// is negative.
public static long GetMaxCompressedLength(long inputLength)
{
- // compressBound() returns the upper bound for zlib-wrapped deflate output, which already
+ // DeflateEncoder.GetMaxCompressedLength() returns a bound for deflate output that already
// accounts for zlib's 2-byte header and 4-byte Adler32 trailer. ZLibEncoder produces
- // zlib-format output, so this value is the exact upper bound with no additional overhead needed.
+ // zlib-format output, so this value is a valid upper bound with no additional overhead needed.
return DeflateEncoder.GetMaxCompressedLength(inputLength);
}