Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting ZipArchiveEntry general-purpose flag bits #98278

Merged
merged 7 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public partial class ZipArchiveEntry
private List<ZipGenericExtraField>? _cdUnknownExtraFields;
private List<ZipGenericExtraField>? _lhUnknownExtraFields;
private byte[] _fileComment;
private readonly CompressionLevel? _compressionLevel;
private readonly CompressionLevel _compressionLevel;

// Initializes a ZipArchiveEntry instance for an existing archive entry.
internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd)
Expand Down Expand Up @@ -86,7 +86,7 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd)

_fileComment = cd.FileComment;

_compressionLevel = MapCompressionLevel(_generalPurposeBitFlag);
_compressionLevel = MapCompressionLevel(_generalPurposeBitFlag, CompressionMethod);
}

// Initializes a ZipArchiveEntry instance for a new archive entry with a specified compression level.
Expand All @@ -98,7 +98,7 @@ internal ZipArchiveEntry(ZipArchive archive, string entryName, CompressionLevel
{
CompressionMethod = CompressionMethodValues.Stored;
}
_generalPurposeBitFlag = MapDeflateCompressionOption(_generalPurposeBitFlag, _compressionLevel);
_generalPurposeBitFlag = MapDeflateCompressionOption(_generalPurposeBitFlag, _compressionLevel, CompressionMethod);
}

// Initializes a ZipArchiveEntry instance for a new archive entry.
Expand All @@ -112,9 +112,9 @@ internal ZipArchiveEntry(ZipArchive archive, string entryName)
_versionMadeByPlatform = CurrentZipPlatform;
_versionMadeBySpecification = ZipVersionNeededValues.Default;
_versionToExtract = ZipVersionNeededValues.Default; // this must happen before following two assignment
_compressionLevel = null;
_generalPurposeBitFlag = MapDeflateCompressionOption(0, _compressionLevel);
_compressionLevel = CompressionLevel.Optimal;
edwardneal marked this conversation as resolved.
Show resolved Hide resolved
CompressionMethod = CompressionMethodValues.Deflate;
_generalPurposeBitFlag = MapDeflateCompressionOption(0, _compressionLevel, CompressionMethod);
_lastModified = DateTimeOffset.Now;

_compressedSize = 0; // we don't know these yet
Expand Down Expand Up @@ -632,7 +632,7 @@ private CheckSumAndSizeWriteStream GetDataCompressor(Stream backingStream, bool
case CompressionMethodValues.Deflate:
case CompressionMethodValues.Deflate64:
default:
compressorStream = new DeflateStream(backingStream, _compressionLevel ?? CompressionLevel.Optimal, leaveBackingStreamOpen);
compressorStream = new DeflateStream(backingStream, _compressionLevel, leaveBackingStreamOpen);
break;

}
Expand Down Expand Up @@ -799,31 +799,39 @@ private bool IsOpenable(bool needToUncompress, bool needToLoadIntoMemory, out st

private bool SizesTooLarge() => _compressedSize > uint.MaxValue || _uncompressedSize > uint.MaxValue;

private static CompressionLevel? MapCompressionLevel(BitFlagValues generalPurposeBitFlag)
private static CompressionLevel MapCompressionLevel(BitFlagValues generalPurposeBitFlag, CompressionMethodValues compressionMethod)
{
// Information about the Deflate compression option is stored in bits 1 and 2 of the general purpose bit flags.
int deflateCompressionOption = (int)generalPurposeBitFlag & 0x6;
// If the compression method is not Deflate, the Deflate compression option is invalid - default to NoCompression.
int deflateCompressionOption = compressionMethod == CompressionMethodValues.Deflate || compressionMethod == CompressionMethodValues.Deflate64
edwardneal marked this conversation as resolved.
Show resolved Hide resolved
? (int)generalPurposeBitFlag & 0x6
: 6;

return deflateCompressionOption switch
return deflateCompressionOption switch
{
0 => CompressionLevel.Optimal,
2 => CompressionLevel.SmallestSize,
4 => CompressionLevel.Fastest,
6 => CompressionLevel.NoCompression,
_ => null
_ => CompressionLevel.Optimal
};
}

private static BitFlagValues MapDeflateCompressionOption(BitFlagValues generalPurposeBitFlag, CompressionLevel? compressionLevel)
private static BitFlagValues MapDeflateCompressionOption(BitFlagValues generalPurposeBitFlag, CompressionLevel compressionLevel, CompressionMethodValues compressionMethod)
{
ushort deflateCompressionOptions = compressionLevel switch
{
CompressionLevel.Optimal => 0,
CompressionLevel.SmallestSize => 2,
CompressionLevel.Fastest => 4,
CompressionLevel.NoCompression => 6,
_ => 0
};
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
? compressionLevel switch
{
CompressionLevel.Optimal => 0,
CompressionLevel.SmallestSize => 2,
CompressionLevel.Fastest => 4,
CompressionLevel.NoCompression => 6,
_ => 0
}
: 0);

return (BitFlagValues)(((int)generalPurposeBitFlag & ~0x6) | deflateCompressionOptions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ public static void CreateUncompressedArchive()
// bit flags correctly. It verifies that these have been set by reading from the MemoryStream manually, and by
// reopening the generated file to confirm that the compression levels match.
[Theory]
[InlineData(CompressionLevel.NoCompression, 6)]
// Special-case NoCompression: in this case, the CompressionMethod becomes Stored and the bits are unset.
[InlineData(CompressionLevel.NoCompression, 0)]
[InlineData(CompressionLevel.Optimal, 0)]
[InlineData(CompressionLevel.SmallestSize, 2)]
[InlineData(CompressionLevel.Fastest, 4)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ internal static void GetZipCompressionMethodFromOpcCompressionOption(
break;
case CompressionOption.Maximum:
{
#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
#if NET
compressionLevel = CompressionLevel.SmallestSize;
#else
compressionLevel = CompressionLevel.Optimal;
Expand Down