diff --git a/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs b/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs
index e0e0bd0eda52f2..6f4c376de6c241 100644
--- a/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs
+++ b/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs
@@ -116,6 +116,7 @@ internal ZipArchiveEntry() { }
[System.Diagnostics.CodeAnalysis.AllowNullAttribute]
public string Comment { get { throw null; } set { } }
public long CompressedLength { get { throw null; } }
+ public System.IO.Compression.ZipCompressionMethod CompressionMethod { get { throw null; } }
[System.CLSCompliantAttribute(false)]
public uint Crc32 { get { throw null; } }
public int ExternalAttributes { get { throw null; } set { } }
@@ -135,6 +136,12 @@ public enum ZipArchiveMode
Create = 1,
Update = 2,
}
+ public enum ZipCompressionMethod
+ {
+ Stored = 0,
+ Deflate = 8,
+ Deflate64 = 9,
+ }
public sealed partial class ZLibCompressionOptions
{
public ZLibCompressionOptions() { }
diff --git a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
index 7510c1c1052274..91ad2914646cd3 100644
--- a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
+++ b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
@@ -54,6 +54,7 @@
+
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateManagedStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateManagedStream.cs
index d161f746677724..dc3c9b5eeffb2a 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateManagedStream.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateManagedStream.cs
@@ -20,16 +20,16 @@ internal sealed partial class DeflateManagedStream : Stream
private int _asyncOperations;
// A specific constructor to allow decompression of Deflate64
- internal DeflateManagedStream(Stream stream, ZipArchiveEntry.CompressionMethodValues method, long uncompressedSize = -1)
+ internal DeflateManagedStream(Stream stream, ZipCompressionMethod method, long uncompressedSize = -1)
{
ArgumentNullException.ThrowIfNull(stream);
if (!stream.CanRead)
throw new ArgumentException(SR.NotSupported_UnreadableStream, nameof(stream));
- Debug.Assert(method == ZipArchiveEntry.CompressionMethodValues.Deflate64);
+ Debug.Assert(method == ZipCompressionMethod.Deflate64);
- _inflater = new InflaterManaged(method == ZipArchiveEntry.CompressionMethodValues.Deflate64, uncompressedSize);
+ _inflater = new InflaterManaged(method == ZipCompressionMethod.Deflate64, uncompressedSize);
_stream = stream;
_buffer = new byte[DefaultBufferSize];
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs
index 030f2d1e25f635..19bdbfff093f20 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs
@@ -89,9 +89,9 @@ private async Task GetUncompressedDataAsync(CancellationToken canc
}
// if they start modifying it and the compression method is not "store", we should make sure it will get deflated
- if (CompressionMethod != CompressionMethodValues.Stored)
+ if (CompressionMethod != ZipCompressionMethod.Stored)
{
- CompressionMethod = CompressionMethodValues.Deflate;
+ CompressionMethod = ZipCompressionMethod.Deflate;
}
}
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs
index fb5733e8ebd28c..c382c5e2c840c5 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs
@@ -24,7 +24,7 @@ public partial class ZipArchiveEntry
private ZipVersionNeededValues _versionToExtract;
private BitFlagValues _generalPurposeBitFlag;
private readonly bool _isEncrypted;
- private CompressionMethodValues _storedCompressionMethod;
+ private ZipCompressionMethod _storedCompressionMethod;
private DateTimeOffset _lastModified;
private long _compressedSize;
private long _uncompressedSize;
@@ -66,7 +66,7 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd)
_versionToExtract = (ZipVersionNeededValues)cd.VersionNeededToExtract;
_generalPurposeBitFlag = (BitFlagValues)cd.GeneralPurposeBitFlag;
_isEncrypted = (_generalPurposeBitFlag & BitFlagValues.IsEncrypted) != 0;
- CompressionMethod = (CompressionMethodValues)cd.CompressionMethod;
+ CompressionMethod = (ZipCompressionMethod)cd.CompressionMethod;
_lastModified = new DateTimeOffset(ZipHelper.DosTimeToDateTime(cd.LastModified));
_compressedSize = cd.CompressedSize;
_uncompressedSize = cd.UncompressedSize;
@@ -104,7 +104,7 @@ internal ZipArchiveEntry(ZipArchive archive, string entryName, CompressionLevel
_compressionLevel = compressionLevel;
if (_compressionLevel == CompressionLevel.NoCompression)
{
- CompressionMethod = CompressionMethodValues.Stored;
+ CompressionMethod = ZipCompressionMethod.Stored;
}
_generalPurposeBitFlag = MapDeflateCompressionOption(_generalPurposeBitFlag, _compressionLevel, CompressionMethod);
}
@@ -121,7 +121,7 @@ internal ZipArchiveEntry(ZipArchive archive, string entryName)
_versionMadeBySpecification = ZipVersionNeededValues.Default;
_versionToExtract = ZipVersionNeededValues.Default; // this must happen before following two assignment
_compressionLevel = CompressionLevel.Optimal;
- CompressionMethod = CompressionMethodValues.Deflate;
+ CompressionMethod = ZipCompressionMethod.Deflate;
_generalPurposeBitFlag = MapDeflateCompressionOption(0, _compressionLevel, CompressionMethod);
_lastModified = DateTimeOffset.Now;
@@ -173,6 +173,22 @@ internal ZipArchiveEntry(ZipArchive archive, string entryName)
///
public bool IsEncrypted => _isEncrypted;
+ ///
+ /// Gets the compression method used to compress the entry.
+ ///
+ public ZipCompressionMethod CompressionMethod
+ {
+ get => _storedCompressionMethod;
+ private set
+ {
+ if (value == ZipCompressionMethod.Deflate)
+ VersionToExtractAtLeast(ZipVersionNeededValues.Deflate);
+ else if (value == ZipCompressionMethod.Deflate64)
+ VersionToExtractAtLeast(ZipVersionNeededValues.Deflate64);
+ _storedCompressionMethod = value;
+ }
+ }
+
///
/// The compressed size of the entry. If the archive that the entry belongs to is in Create mode, attempts to get this property will always throw an exception. If the archive that the entry belongs to is in update mode, this property will only be valid if the entry has not been opened.
///
@@ -439,27 +455,15 @@ private MemoryStream GetUncompressedData()
}
// if they start modifying it and the compression method is not "store", we should make sure it will get deflated
- if (CompressionMethod != CompressionMethodValues.Stored)
+ if (CompressionMethod != ZipCompressionMethod.Stored)
{
- CompressionMethod = CompressionMethodValues.Deflate;
+ CompressionMethod = ZipCompressionMethod.Deflate;
}
}
return _storedUncompressedData;
}
- private CompressionMethodValues CompressionMethod
- {
- get { return _storedCompressionMethod; }
- set
- {
- if (value == CompressionMethodValues.Deflate)
- VersionToExtractAtLeast(ZipVersionNeededValues.Deflate);
- else if (value == CompressionMethodValues.Deflate64)
- VersionToExtractAtLeast(ZipVersionNeededValues.Deflate64);
- _storedCompressionMethod = value;
- }
- }
// does almost everything you need to do to forget about this entry
// writes the local header/data, gets rid of all the data,
// closes all of the streams except for the very outermost one that
@@ -714,20 +718,20 @@ private CheckSumAndSizeWriteStream GetDataCompressor(Stream backingStream, bool
// changed to Stored.
//
// Note: Deflate64 is not supported on all platforms
- Debug.Assert(CompressionMethod == CompressionMethodValues.Deflate
- || CompressionMethod == CompressionMethodValues.Stored);
+ Debug.Assert(CompressionMethod == ZipCompressionMethod.Deflate
+ || CompressionMethod == ZipCompressionMethod.Stored);
Func compressorStreamFactory;
bool isIntermediateStream = true;
switch (CompressionMethod)
{
- case CompressionMethodValues.Stored:
+ case ZipCompressionMethod.Stored:
compressorStreamFactory = () => backingStream;
isIntermediateStream = false;
break;
- case CompressionMethodValues.Deflate:
- case CompressionMethodValues.Deflate64:
+ case ZipCompressionMethod.Deflate:
+ case ZipCompressionMethod.Deflate64:
default:
compressorStreamFactory = () => new DeflateStream(backingStream, _compressionLevel, leaveBackingStreamOpen);
break;
@@ -756,17 +760,17 @@ private Stream GetDataDecompressor(Stream compressedStreamToRead)
Stream? uncompressedStream;
switch (CompressionMethod)
{
- case CompressionMethodValues.Deflate:
+ case ZipCompressionMethod.Deflate:
uncompressedStream = new DeflateStream(compressedStreamToRead, CompressionMode.Decompress, _uncompressedSize);
break;
- case CompressionMethodValues.Deflate64:
- uncompressedStream = new DeflateManagedStream(compressedStreamToRead, CompressionMethodValues.Deflate64, _uncompressedSize);
+ case ZipCompressionMethod.Deflate64:
+ uncompressedStream = new DeflateManagedStream(compressedStreamToRead, ZipCompressionMethod.Deflate64, _uncompressedSize);
break;
- case CompressionMethodValues.Stored:
+ case ZipCompressionMethod.Stored:
default:
// we can assume that only deflate/deflate64/stored are allowed because we assume that
// IsOpenable is checked before this function is called
- Debug.Assert(CompressionMethod == CompressionMethodValues.Stored);
+ Debug.Assert(CompressionMethod == ZipCompressionMethod.Stored);
uncompressedStream = compressedStreamToRead;
break;
@@ -867,15 +871,11 @@ private bool IsOpenableInitialVerifications(bool needToUncompress, out string? m
message = null;
if (needToUncompress)
{
- if (CompressionMethod != CompressionMethodValues.Stored &&
- CompressionMethod != CompressionMethodValues.Deflate &&
- CompressionMethod != CompressionMethodValues.Deflate64)
+ if (CompressionMethod != ZipCompressionMethod.Stored &&
+ CompressionMethod != ZipCompressionMethod.Deflate &&
+ CompressionMethod != ZipCompressionMethod.Deflate64)
{
- message = CompressionMethod switch
- {
- CompressionMethodValues.BZip2 or CompressionMethodValues.LZMA => SR.Format(SR.UnsupportedCompressionMethod, CompressionMethod.ToString()),
- _ => SR.UnsupportedCompression,
- };
+ message = SR.UnsupportedCompression;
return false;
}
}
@@ -923,11 +923,11 @@ private bool IsOpenableFinalVerifications(bool needToLoadIntoMemory, long offset
private bool AreSizesTooLarge => _compressedSize > uint.MaxValue || _uncompressedSize > uint.MaxValue;
- private static CompressionLevel MapCompressionLevel(BitFlagValues generalPurposeBitFlag, CompressionMethodValues compressionMethod)
+ private static CompressionLevel MapCompressionLevel(BitFlagValues generalPurposeBitFlag, ZipCompressionMethod compressionMethod)
{
// Information about the Deflate compression option is stored in bits 1 and 2 of the general purpose bit flags.
// If the compression method is not Deflate, the Deflate compression option is invalid - default to NoCompression.
- if (compressionMethod == CompressionMethodValues.Deflate || compressionMethod == CompressionMethodValues.Deflate64)
+ if (compressionMethod == ZipCompressionMethod.Deflate || compressionMethod == ZipCompressionMethod.Deflate64)
{
return ((int)generalPurposeBitFlag & 0x6) switch
{
@@ -944,12 +944,12 @@ private static CompressionLevel MapCompressionLevel(BitFlagValues generalPurpose
}
}
- private static BitFlagValues MapDeflateCompressionOption(BitFlagValues generalPurposeBitFlag, CompressionLevel compressionLevel, CompressionMethodValues compressionMethod)
+ private static BitFlagValues MapDeflateCompressionOption(BitFlagValues generalPurposeBitFlag, CompressionLevel compressionLevel, ZipCompressionMethod compressionMethod)
{
ushort deflateCompressionOptions = (ushort)(
// The Deflate compression level is only valid if the compression method is actually Deflate (or Deflate64). If it's not, the
// value of the two bits is undefined and they should be zeroed out.
- compressionMethod == CompressionMethodValues.Deflate || compressionMethod == CompressionMethodValues.Deflate64
+ compressionMethod == ZipCompressionMethod.Deflate || compressionMethod == ZipCompressionMethod.Deflate64
? compressionLevel switch
{
CompressionLevel.Optimal => 0,
@@ -982,7 +982,7 @@ private bool WriteLocalFileHeaderInitialize(bool isEmptyFile, bool forceWrite, o
// if we already know that we have an empty file don't worry about anything, just do a straight shot of the header
if (isEmptyFile)
{
- CompressionMethod = CompressionMethodValues.Stored;
+ CompressionMethod = ZipCompressionMethod.Stored;
compressedSizeTruncated = 0;
uncompressedSizeTruncated = 0;
Debug.Assert(_uncompressedSize == 0);
@@ -1627,15 +1627,6 @@ internal enum BitFlagValues : ushort
UnicodeFileNameAndComment = 0x800
}
- internal enum CompressionMethodValues : ushort
- {
- Stored = 0x0,
- Deflate = 0x8,
- Deflate64 = 0x9,
- BZip2 = 0xC,
- LZMA = 0xE
- }
-
internal sealed class LocalHeaderOffsetComparer : Comparer
{
private static readonly LocalHeaderOffsetComparer s_instance = new LocalHeaderOffsetComparer();
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCompressionMethod.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCompressionMethod.cs
new file mode 100644
index 00000000000000..7ca56e277bbd2f
--- /dev/null
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCompressionMethod.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.IO.Compression
+{
+ ///
+ /// Specifies the compression method used to compress an entry in a zip archive.
+ ///
+ ///
+ /// The values correspond to the compression method values described in the ZIP File Format Specification (APPNOTE.TXT section 4.4.5).
+ ///
+ public enum ZipCompressionMethod
+ {
+ ///
+ /// The entry is stored (no compression).
+ ///
+ Stored = 0x0,
+
+ ///
+ /// The entry is compressed using the Deflate algorithm.
+ ///
+ Deflate = 0x8,
+
+ ///
+ /// The entry is compressed using the Deflate64 algorithm.
+ ///
+ Deflate64 = 0x9,
+ }
+}
diff --git a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs
index 24e96ba0dd594b..5218765645a3a7 100644
--- a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs
+++ b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs
@@ -595,10 +595,9 @@ public static async Task ReadStreamOps(bool async)
// Check the entry's compression method to determine seekability
// SubReadStream should be seekable when the underlying stream is seekable and the entry is stored (uncompressed)
// If the entry is compressed (Deflate, Deflate64, etc.), it will be wrapped in a compression stream which is not seekable
- ushort compressionMethod = (ushort)compressionMethodField.GetValue(e);
- const ushort StoredCompressionMethod = 0x0; // CompressionMethodValues.Stored
+ ZipCompressionMethod compressionMethod = (ZipCompressionMethod)compressionMethodField.GetValue(e);
- if (compressionMethod == StoredCompressionMethod)
+ if (compressionMethod == ZipCompressionMethod.Stored)
{
// Entry is stored (uncompressed), should be seekable
Assert.True(s.CanSeek, $"SubReadStream should be seekable for stored (uncompressed) entry '{e.FullName}' with compression method {compressionMethod} when underlying stream is seekable");
@@ -820,5 +819,75 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
}
+
+ [Theory]
+ [MemberData(nameof(Get_Booleans_Data))]
+ public static async Task CompressionMethod_Deflate_ReturnsDeflate(bool async)
+ {
+ using var ms = new MemoryStream();
+ using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, leaveOpen: true))
+ {
+ var entry = archive.CreateEntry("test.txt", CompressionLevel.Optimal);
+ using (var stream = entry.Open())
+ {
+ stream.Write("test data"u8);
+ }
+ }
+
+ ms.Position = 0;
+ ZipArchive readArchive = await CreateZipArchive(async, ms, ZipArchiveMode.Read);
+ ZipArchiveEntry readEntry = readArchive.Entries[0];
+ Assert.Equal(ZipCompressionMethod.Deflate, readEntry.CompressionMethod);
+ await DisposeZipArchive(async, readArchive);
+ }
+
+ [Theory]
+ [MemberData(nameof(Get_Booleans_Data))]
+ public static async Task CompressionMethod_Stored_ReturnsStored(bool async)
+ {
+ using var ms = new MemoryStream();
+ using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, leaveOpen: true))
+ {
+ var entry = archive.CreateEntry("test.txt", CompressionLevel.NoCompression);
+ using (var stream = entry.Open())
+ {
+ stream.Write("test data"u8);
+ }
+ }
+
+ ms.Position = 0;
+ ZipArchive readArchive = await CreateZipArchive(async, ms, ZipArchiveMode.Read);
+ ZipArchiveEntry readEntry = readArchive.Entries[0];
+ Assert.Equal(ZipCompressionMethod.Stored, readEntry.CompressionMethod);
+ await DisposeZipArchive(async, readArchive);
+ }
+
+ [Theory]
+ [MemberData(nameof(Get_Booleans_Data))]
+ public static async Task CompressionMethod_EmptyFile_ReturnsStored(bool async)
+ {
+ using var ms = new MemoryStream();
+ using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, leaveOpen: true))
+ {
+ var entry = archive.CreateEntry("empty.txt");
+ }
+
+ ms.Position = 0;
+ ZipArchive readArchive = await CreateZipArchive(async, ms, ZipArchiveMode.Read);
+ ZipArchiveEntry readEntry = readArchive.Entries[0];
+ Assert.Equal(ZipCompressionMethod.Stored, readEntry.CompressionMethod);
+ await DisposeZipArchive(async, readArchive);
+ }
+
+ [Theory]
+ [MemberData(nameof(Get_Booleans_Data))]
+ public static async Task CompressionMethod_Deflate64_ReturnsDeflate64(bool async)
+ {
+ MemoryStream ms = await StreamHelpers.CreateTempCopyStream(compat("deflate64.zip"));
+ ZipArchive readArchive = await CreateZipArchive(async, ms, ZipArchiveMode.Read);
+ ZipArchiveEntry readEntry = readArchive.Entries[0];
+ Assert.Equal(ZipCompressionMethod.Deflate64, readEntry.CompressionMethod);
+ await DisposeZipArchive(async, readArchive);
+ }
}
}