diff --git a/src/libraries/Common/src/Interop/Interop.Brotli.cs b/src/libraries/Common/src/Interop/Interop.Brotli.cs
index cc49e4b323a53..31d6a94d6fdde 100644
--- a/src/libraries/Common/src/Interop/Interop.Brotli.cs
+++ b/src/libraries/Common/src/Interop/Interop.Brotli.cs
@@ -41,6 +41,9 @@ internal static unsafe partial BOOL BrotliEncoderCompressStream(
[LibraryImport(Libraries.CompressionNative)]
internal static partial BOOL BrotliEncoderHasMoreOutput(SafeBrotliEncoderHandle state);
+ [LibraryImport(Libraries.CompressionNative)]
+ internal static partial nuint BrotliEncoderMaxCompressedSize(nuint inputSize);
+
[LibraryImport(Libraries.CompressionNative)]
internal static partial void BrotliEncoderDestroyInstance(IntPtr state);
diff --git a/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliUtils.cs b/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliUtils.cs
index 2e8e55a570ae3..3ddf4d2a28826 100644
--- a/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliUtils.cs
+++ b/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliUtils.cs
@@ -11,7 +11,7 @@ internal static partial class BrotliUtils
public const int Quality_Min = 0;
public const int Quality_Default = 4;
public const int Quality_Max = 11;
- public const int MaxInputSize = int.MaxValue - 515; // 515 is the max compressed extra bytes
+ public const int MaxInputSize = int.MaxValue - 524_166; // 524_166 is the max compressed extra bytes
internal static int GetQualityFromCompressionLevel(CompressionLevel compressionLevel) =>
compressionLevel switch
diff --git a/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoder.cs b/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoder.cs
index eb25871c69e04..3ad7309aac7ea 100644
--- a/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoder.cs
+++ b/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoder.cs
@@ -103,26 +103,16 @@ internal void SetWindow(int window)
}
/// Gets the maximum expected compressed length for the provided input size.
- /// The input size to get the maximum expected compressed length from. Must be greater or equal than 0 and less or equal than - 515.
+ /// The input size to get the maximum expected compressed length from. Must be greater or equal than 0 and less or equal than - 524166.
/// A number representing the maximum compressed length for the provided input size.
- /// Returns 1 if is 0.
- /// is less than 0, the minimum allowed input size, or greater than - 515, the maximum allowed input size.
+ /// Returns 2 if is 0.
+ /// is less than 0, the minimum allowed input size, or greater than - 524166, the maximum allowed input size.
public static int GetMaxCompressedLength(int inputSize)
{
ArgumentOutOfRangeException.ThrowIfNegative(inputSize);
ArgumentOutOfRangeException.ThrowIfGreaterThan(inputSize, BrotliUtils.MaxInputSize);
- if (inputSize == 0)
- {
- return 1;
- }
-
- int numLargeBlocks = inputSize >> 24;
- int tail = inputSize & 0xFFFFFF;
- int tailOverhead = (tail > (1 << 20)) ? 4 : 3;
- int overhead = 2 + (4 * numLargeBlocks) + tailOverhead + 1;
- int result = inputSize + overhead;
- return result;
+ return (int)Interop.Brotli.BrotliEncoderMaxCompressedSize((nuint)inputSize);
}
internal OperationStatus Flush(Memory destination, out int bytesWritten) => Flush(destination.Span, out bytesWritten);
diff --git a/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs b/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs
index e3a7f2b830644..5b992cfcb246a 100644
--- a/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs
+++ b/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs
@@ -78,9 +78,23 @@ public void InvalidWindow()
public void GetMaxCompressedSize_Basic()
{
Assert.Throws("inputSize", () => BrotliEncoder.GetMaxCompressedLength(-1));
- Assert.Throws("inputSize", () => BrotliEncoder.GetMaxCompressedLength(2147483133));
- Assert.InRange(BrotliEncoder.GetMaxCompressedLength(2147483132), 0, int.MaxValue);
- Assert.Equal(1, BrotliEncoder.GetMaxCompressedLength(0));
+ Assert.Throws("inputSize", () => BrotliEncoder.GetMaxCompressedLength(2_146_959_482));
+ Assert.InRange(BrotliEncoder.GetMaxCompressedLength(2_146_959_481), 0, int.MaxValue); // 2_146_959_481 produces int.MaxValue
+ Assert.Equal(2, BrotliEncoder.GetMaxCompressedLength(0));
+ }
+
+ [Fact]
+ public void DestinationBufferWithSizeEqualToMaxCompressedLength_ShouldAlwaysSucceed()
+ {
+ byte[] source = new byte[256000];
+ var rng = new Random(1234);
+ rng.NextBytes(source);
+
+ int maxLength = BrotliEncoder.GetMaxCompressedLength(source.Length);
+ var resultBuffer = new byte[maxLength];
+
+ Assert.True(BrotliEncoder.TryCompress(source, resultBuffer, out int bytesWritten, quality: 5, window: 10));
+ Assert.True(maxLength >= bytesWritten);
}
[Fact]
diff --git a/src/native/libs/System.IO.Compression.Native/System.IO.Compression.Native.def b/src/native/libs/System.IO.Compression.Native/System.IO.Compression.Native.def
index 6821d0e538f51..05da5501d8582 100644
--- a/src/native/libs/System.IO.Compression.Native/System.IO.Compression.Native.def
+++ b/src/native/libs/System.IO.Compression.Native/System.IO.Compression.Native.def
@@ -11,6 +11,7 @@ EXPORTS
BrotliEncoderCreateInstance
BrotliEncoderDestroyInstance
BrotliEncoderHasMoreOutput
+ BrotliEncoderMaxCompressedSize
BrotliEncoderSetParameter
CompressionNative_Crc32
CompressionNative_Deflate
diff --git a/src/native/libs/System.IO.Compression.Native/System.IO.Compression.Native_unixexports.src b/src/native/libs/System.IO.Compression.Native/System.IO.Compression.Native_unixexports.src
index 08dd1700a52f2..0205afdf9a95d 100644
--- a/src/native/libs/System.IO.Compression.Native/System.IO.Compression.Native_unixexports.src
+++ b/src/native/libs/System.IO.Compression.Native/System.IO.Compression.Native_unixexports.src
@@ -11,6 +11,7 @@ BrotliEncoderCompressStream
BrotliEncoderCreateInstance
BrotliEncoderDestroyInstance
BrotliEncoderHasMoreOutput
+BrotliEncoderMaxCompressedSize
BrotliEncoderSetParameter
CompressionNative_Crc32
CompressionNative_Deflate
diff --git a/src/native/libs/System.IO.Compression.Native/entrypoints.c b/src/native/libs/System.IO.Compression.Native/entrypoints.c
index 05a8c228eb785..2f33eb6fb228b 100644
--- a/src/native/libs/System.IO.Compression.Native/entrypoints.c
+++ b/src/native/libs/System.IO.Compression.Native/entrypoints.c
@@ -22,6 +22,7 @@ static const Entry s_compressionNative[] =
DllImportEntry(BrotliEncoderCreateInstance)
DllImportEntry(BrotliEncoderDestroyInstance)
DllImportEntry(BrotliEncoderHasMoreOutput)
+ DllImportEntry(BrotliEncoderMaxCompressedSize)
DllImportEntry(BrotliEncoderSetParameter)
DllImportEntry(CompressionNative_Crc32)
DllImportEntry(CompressionNative_Deflate)