diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs index f0b289954012c4..22cd16b6fafdac 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs @@ -530,14 +530,10 @@ private void ReadCentralDirectory() while (continueReadingCentralDirectory && currPosition + ZipCentralDirectoryFileHeader.BlockConstantSectionSize < sizedFileBuffer.Length) { - ZipCentralDirectoryFileHeader currentHeader = default; - - continueReadingCentralDirectory = continueReadingCentralDirectory && - ZipCentralDirectoryFileHeader.TryReadBlock(sizedFileBuffer.Slice(currPosition), _archiveStream, - saveExtraFieldsAndComments, out bytesConsumed, out currentHeader); - - if (!continueReadingCentralDirectory) + if (!ZipCentralDirectoryFileHeader.TryReadBlock(sizedFileBuffer.Slice(currPosition), _archiveStream, + saveExtraFieldsAndComments, out bytesConsumed, out ZipCentralDirectoryFileHeader? currentHeader)) { + continueReadingCentralDirectory = false; break; } @@ -662,8 +658,7 @@ private void TryReadZip64EndOfCentralDirectory(ZipEndOfCentralDirectoryBlock eoc Zip64EndOfCentralDirectoryLocator.FieldLengths.Signature)) { // use locator to get to Zip64-EOCD - Zip64EndOfCentralDirectoryLocator locator; - bool zip64eocdLocatorProper = Zip64EndOfCentralDirectoryLocator.TryReadBlock(_archiveStream, out locator); + bool zip64eocdLocatorProper = Zip64EndOfCentralDirectoryLocator.TryReadBlock(_archiveStream, out Zip64EndOfCentralDirectoryLocator locator); Debug.Assert(zip64eocdLocatorProper); // we just found this using the signature finder, so it should be okay if (locator.OffsetOfZip64EOCD > long.MaxValue) 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 93250857111adf..2ff22deca89144 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 @@ -491,24 +491,24 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite) Debug.Assert(_fileComment.Length <= ushort.MaxValue); // decide if we need the Zip64 extra field: - Zip64ExtraField zip64ExtraField = default; + Zip64ExtraField? zip64ExtraField = null; uint compressedSizeTruncated, uncompressedSizeTruncated, offsetOfLocalHeaderTruncated; - bool zip64Needed = false; - if (AreSizesTooLarge #if DEBUG_FORCE_ZIP64 || _archive._forceZip64 #endif ) { - zip64Needed = true; compressedSizeTruncated = ZipHelper.Mask32Bit; uncompressedSizeTruncated = ZipHelper.Mask32Bit; // If we have one of the sizes, the other must go in there as speced for LH, but not necessarily for CH, but we do it anyways - zip64ExtraField.CompressedSize = _compressedSize; - zip64ExtraField.UncompressedSize = _uncompressedSize; + zip64ExtraField = new() + { + CompressedSize = _compressedSize, + UncompressedSize = _uncompressedSize + }; } else { @@ -523,27 +523,32 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite) #endif ) { - zip64Needed = true; offsetOfLocalHeaderTruncated = ZipHelper.Mask32Bit; // If we have one of the sizes, the other must go in there as speced for LH, but not necessarily for CH, but we do it anyways - zip64ExtraField.LocalHeaderOffset = _offsetOfLocalHeader; + zip64ExtraField = new() + { + LocalHeaderOffset = _offsetOfLocalHeader + }; } else { offsetOfLocalHeaderTruncated = (uint)_offsetOfLocalHeader; } - if (zip64Needed) + if (zip64ExtraField != null) + { VersionToExtractAtLeast(ZipVersionNeededValues.Zip64); + } + // determine if we can fit zip64 extra field and original extra fields all in - int bigExtraFieldLength = (zip64Needed ? zip64ExtraField.TotalSize : 0) + int bigExtraFieldLength = (zip64ExtraField != null ? zip64ExtraField.TotalSize : 0) + (_cdUnknownExtraFields != null ? ZipGenericExtraField.TotalSize(_cdUnknownExtraFields) : 0); ushort extraFieldLength; if (bigExtraFieldLength > ushort.MaxValue) { - extraFieldLength = (ushort)(zip64Needed ? zip64ExtraField.TotalSize : 0); + extraFieldLength = (ushort)(zip64ExtraField != null ? zip64ExtraField.TotalSize : 0); _cdUnknownExtraFields = null; } else @@ -555,7 +560,7 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite) { long centralDirectoryHeaderLength = ZipCentralDirectoryFileHeader.FieldLocations.DynamicData + _storedEntryNameBytes.Length - + (zip64Needed ? zip64ExtraField.TotalSize : 0) + + (zip64ExtraField != null ? zip64ExtraField.TotalSize : 0) + (_cdUnknownExtraFields != null ? ZipGenericExtraField.TotalSize(_cdUnknownExtraFields) : 0) + _fileComment.Length; @@ -604,14 +609,18 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite) _archive.ArchiveStream.Write(cdStaticHeader); _archive.ArchiveStream.Write(_storedEntryNameBytes); - // write extra fields - if (zip64Needed) - zip64ExtraField.WriteBlock(_archive.ArchiveStream); + // write extra fields, and only write zip64ExtraField if we decided we need it (it's not null) + zip64ExtraField?.WriteBlock(_archive.ArchiveStream); + if (_cdUnknownExtraFields != null) + { ZipGenericExtraField.WriteAllBlocks(_cdUnknownExtraFields, _archive.ArchiveStream); + } if (_fileComment.Length > 0) + { _archive.ArchiveStream.Write(_fileComment); + } } } @@ -908,8 +917,7 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite) Debug.Assert(_storedEntryNameBytes.Length <= ushort.MaxValue); // decide if we need the Zip64 extra field: - Zip64ExtraField zip64ExtraField = default; - bool zip64Used = false; + Zip64ExtraField? zip64ExtraField = null; uint compressedSizeTruncated, uncompressedSizeTruncated; // save offset @@ -932,7 +940,6 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite) if (_archive.Mode == ZipArchiveMode.Create && _archive.ArchiveStream.CanSeek == false) { _generalPurposeBitFlag |= BitFlagValues.DataDescriptor; - zip64Used = false; compressedSizeTruncated = 0; uncompressedSizeTruncated = 0; // the crc should not have been set if we are in create mode, but clear it just to be sure @@ -948,19 +955,20 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite) #endif ) { - zip64Used = true; compressedSizeTruncated = ZipHelper.Mask32Bit; uncompressedSizeTruncated = ZipHelper.Mask32Bit; // prepare Zip64 extra field object. If we have one of the sizes, the other must go in there - zip64ExtraField.CompressedSize = _compressedSize; - zip64ExtraField.UncompressedSize = _uncompressedSize; + zip64ExtraField = new() + { + CompressedSize = _compressedSize, + UncompressedSize = _uncompressedSize, + }; VersionToExtractAtLeast(ZipVersionNeededValues.Zip64); } else { - zip64Used = false; compressedSizeTruncated = (uint)_compressedSize; uncompressedSizeTruncated = (uint)_uncompressedSize; } @@ -971,12 +979,12 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite) _offsetOfLocalHeader = _archive.ArchiveStream.Position; // calculate extra field. if zip64 stuff + original extraField aren't going to fit, dump the original extraField, because this is more important - int bigExtraFieldLength = (zip64Used ? zip64ExtraField.TotalSize : 0) + int bigExtraFieldLength = (zip64ExtraField != null ? zip64ExtraField.TotalSize : 0) + (_lhUnknownExtraFields != null ? ZipGenericExtraField.TotalSize(_lhUnknownExtraFields) : 0); ushort extraFieldLength; if (bigExtraFieldLength > ushort.MaxValue) { - extraFieldLength = (ushort)(zip64Used ? zip64ExtraField.TotalSize : 0); + extraFieldLength = (ushort)(zip64ExtraField != null ? zip64ExtraField.TotalSize : 0); _lhUnknownExtraFields = null; } else @@ -990,7 +998,7 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite) { _archive.ArchiveStream.Seek(ZipLocalFileHeader.SizeOfLocalHeader + _storedEntryNameBytes.Length, SeekOrigin.Current); - if (zip64Used) + if (zip64ExtraField != null) { _archive.ArchiveStream.Seek(zip64ExtraField.TotalSize, SeekOrigin.Current); } @@ -1018,13 +1026,14 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite) _archive.ArchiveStream.Write(_storedEntryNameBytes); - if (zip64Used) - zip64ExtraField.WriteBlock(_archive.ArchiveStream); + // Only when handling zip64 + zip64ExtraField?.WriteBlock(_archive.ArchiveStream); + if (_lhUnknownExtraFields != null) ZipGenericExtraField.WriteAllBlocks(_lhUnknownExtraFields, _archive.ArchiveStream); } - return zip64Used; + return zip64ExtraField != null; } private void WriteLocalFileHeaderAndDataIfNeeded(bool forceWrite) diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.FieldLengths.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.FieldLengths.cs index 6ccf16c82522fe..62b31842d67cce 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.FieldLengths.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.FieldLengths.cs @@ -3,7 +3,7 @@ namespace System.IO.Compression { - internal partial struct ZipGenericExtraField + internal sealed partial class ZipGenericExtraField { private static class FieldLengths { @@ -12,7 +12,7 @@ private static class FieldLengths } } - internal partial struct Zip64ExtraField + internal sealed partial class Zip64ExtraField { internal static class FieldLengths { @@ -23,7 +23,7 @@ internal static class FieldLengths } } - internal partial struct Zip64EndOfCentralDirectoryLocator + internal sealed partial class Zip64EndOfCentralDirectoryLocator { internal static class FieldLengths { @@ -34,7 +34,7 @@ internal static class FieldLengths } } - internal partial struct Zip64EndOfCentralDirectoryRecord + internal sealed partial class Zip64EndOfCentralDirectoryRecord { private static class FieldLengths { @@ -67,7 +67,7 @@ internal static class FieldLengths public const int ExtraFieldLength = sizeof(ushort); } - internal readonly partial struct ZipDataDescriptor + internal sealed partial class ZipDataDescriptor { internal static class FieldLengths { @@ -78,7 +78,7 @@ internal static class FieldLengths } } - internal readonly partial struct Zip64DataDescriptor + internal sealed partial class Zip64DataDescriptor { internal static class FieldLengths { @@ -90,7 +90,7 @@ internal static class FieldLengths } } - internal partial struct ZipCentralDirectoryFileHeader + internal sealed partial class ZipCentralDirectoryFileHeader { internal static class FieldLengths { @@ -114,7 +114,7 @@ internal static class FieldLengths } } - internal partial struct ZipEndOfCentralDirectoryBlock + internal sealed partial class ZipEndOfCentralDirectoryBlock { internal static class FieldLengths { diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.FieldLocations.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.FieldLocations.cs index d2cc3376f30e3a..5d7a5127ed5722 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.FieldLocations.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.FieldLocations.cs @@ -3,7 +3,7 @@ namespace System.IO.Compression { - internal partial struct ZipGenericExtraField + internal sealed partial class ZipGenericExtraField { internal static class FieldLocations { @@ -13,7 +13,7 @@ internal static class FieldLocations } } - internal partial struct Zip64ExtraField + internal sealed partial class Zip64ExtraField { internal static class FieldLocations { @@ -26,7 +26,7 @@ internal static class FieldLocations } } - internal partial struct Zip64EndOfCentralDirectoryLocator + internal sealed partial class Zip64EndOfCentralDirectoryLocator { private static class FieldLocations { @@ -37,7 +37,7 @@ private static class FieldLocations } } - internal partial struct Zip64EndOfCentralDirectoryRecord + internal sealed partial class Zip64EndOfCentralDirectoryRecord { private static class FieldLocations { @@ -71,7 +71,7 @@ internal static class FieldLocations public static readonly int DynamicData = ExtraFieldLength + FieldLengths.ExtraFieldLength; } - internal readonly partial struct ZipDataDescriptor + internal sealed partial class ZipDataDescriptor { internal static class FieldLocations { @@ -82,7 +82,7 @@ internal static class FieldLocations } } - internal readonly partial struct Zip64DataDescriptor + internal sealed partial class Zip64DataDescriptor { internal static class FieldLocations { @@ -94,7 +94,7 @@ internal static class FieldLocations } } - internal partial struct ZipCentralDirectoryFileHeader + internal sealed partial class ZipCentralDirectoryFileHeader { internal static class FieldLocations { @@ -119,7 +119,7 @@ internal static class FieldLocations } } - internal partial struct ZipEndOfCentralDirectoryBlock + internal sealed partial class ZipEndOfCentralDirectoryBlock { private static class FieldLocations { diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs index 6365fafab66b96..6d978f66d9012a 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs @@ -4,24 +4,24 @@ using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.IO.Compression { // All blocks.TryReadBlock do a check to see if signature is correct. Generic extra field is slightly different // all of the TryReadBlocks will throw if there are not enough bytes in the stream - - internal partial struct ZipGenericExtraField + internal sealed partial class ZipGenericExtraField { private const int SizeOfHeader = FieldLengths.Tag + FieldLengths.Size; private ushort _tag; private ushort _size; - private byte[] _data; + private byte[]? _data; public ushort Tag => _tag; // returns size of data, not of the entire block public ushort Size => _size; - public byte[] Data => _data; + public byte[] Data => _data ??= []; public void WriteBlock(Stream stream) { @@ -37,7 +37,7 @@ public void WriteBlock(Stream stream) // assumes that bytes starts at the beginning of an extra field subfield public static bool TryReadBlock(ReadOnlySpan bytes, out int bytesConsumed, out ZipGenericExtraField field) { - field = default; + field = new(); bytesConsumed = 0; // not enough bytes to read tag + size @@ -94,7 +94,7 @@ public static void WriteAllBlocks(List fields, Stream stre } } - internal partial struct Zip64ExtraField + internal sealed partial class Zip64ExtraField { // Size is size of the record not including the tag or size fields // If the extra field is going in the local header, it cannot include only @@ -180,12 +180,13 @@ public static Zip64ExtraField GetJustZip64Block(ReadOnlySpan extraFieldDat } } - zip64Field = default; - - zip64Field._compressedSize = null; - zip64Field._uncompressedSize = null; - zip64Field._localHeaderOffset = null; - zip64Field._startDiskNumber = null; + zip64Field = new() + { + _compressedSize = null, + _uncompressedSize = null, + _localHeaderOffset = null, + _startDiskNumber = null, + }; return zip64Field; } @@ -196,12 +197,13 @@ private static bool TryGetZip64BlockFromGenericExtraField(ZipGenericExtraField e out Zip64ExtraField zip64Block) { const int MaximumExtraFieldLength = FieldLengths.UncompressedSize + FieldLengths.CompressedSize + FieldLengths.LocalHeaderOffset + FieldLengths.StartDiskNumber; - zip64Block = default; - - zip64Block._compressedSize = null; - zip64Block._uncompressedSize = null; - zip64Block._localHeaderOffset = null; - zip64Block._startDiskNumber = null; + zip64Block = new() + { + _compressedSize = null, + _uncompressedSize = null, + _localHeaderOffset = null, + _startDiskNumber = null, + }; if (extraField.Tag != TagConstant) { @@ -302,12 +304,13 @@ public static Zip64ExtraField GetAndRemoveZip64Block(List bool readUncompressedSize, bool readCompressedSize, bool readLocalHeaderOffset, bool readStartDiskNumber) { - Zip64ExtraField zip64Field = default; - - zip64Field._compressedSize = null; - zip64Field._uncompressedSize = null; - zip64Field._localHeaderOffset = null; - zip64Field._startDiskNumber = null; + Zip64ExtraField zip64Field = new() + { + _compressedSize = null, + _uncompressedSize = null, + _localHeaderOffset = null, + _startDiskNumber = null, + }; bool zip64FieldFound = false; @@ -373,7 +376,7 @@ public void WriteBlock(Stream stream) } } - internal partial struct Zip64EndOfCentralDirectoryLocator + internal sealed partial class Zip64EndOfCentralDirectoryLocator { // The Zip File Format Specification references 0x07064B50, this is a big endian representation. // ZIP files store values in little endian, so this is reversed. @@ -391,7 +394,7 @@ public static bool TryReadBlock(Stream stream, out Zip64EndOfCentralDirectoryLoc Span blockContents = stackalloc byte[TotalSize]; int bytesRead; - zip64EOCDLocator = default; + zip64EOCDLocator = new(); bytesRead = stream.Read(blockContents); if (bytesRead < TotalSize) @@ -426,7 +429,7 @@ public static void WriteBlock(Stream stream, long zip64EOCDRecordStart) } } - internal partial struct Zip64EndOfCentralDirectoryRecord + internal sealed partial class Zip64EndOfCentralDirectoryRecord { // The Zip File Format Specification references 0x06064B50, this is a big endian representation. // ZIP files store values in little endian, so this is reversed. @@ -451,7 +454,7 @@ public static bool TryReadBlock(Stream stream, out Zip64EndOfCentralDirectoryRec Span blockContents = stackalloc byte[BlockConstantSectionSize]; int bytesRead; - zip64EOCDRecord = default; + zip64EOCDRecord = new(); bytesRead = stream.Read(blockContents); if (bytesRead < BlockConstantSectionSize) @@ -591,7 +594,7 @@ public static bool TrySkipBlock(Stream stream) } } - internal partial struct ZipCentralDirectoryFileHeader + internal sealed partial class ZipCentralDirectoryFileHeader { // The Zip File Format Specification references 0x02014B50, this is a big endian representation. // ZIP files store values in little endian, so this is reversed. @@ -617,17 +620,18 @@ internal partial struct ZipCentralDirectoryFileHeader public uint ExternalFileAttributes; public long RelativeOffsetOfLocalHeader; - public byte[] Filename; - public byte[] FileComment; + public byte[] Filename = []; + public byte[] FileComment = []; public List? ExtraFields; // if saveExtraFieldsAndComments is false, FileComment and ExtraFields will be null // in either case, the zip64 extra field info will be incorporated into other fields - public static bool TryReadBlock(ReadOnlySpan buffer, Stream furtherReads, bool saveExtraFieldsAndComments, out int bytesRead, out ZipCentralDirectoryFileHeader header) + public static bool TryReadBlock(ReadOnlySpan buffer, Stream furtherReads, bool saveExtraFieldsAndComments, out int bytesRead, [NotNullWhen(returnValue: true)] out ZipCentralDirectoryFileHeader? header) { + header = null; + const int StackAllocationThreshold = 512; - header = default; bytesRead = 0; // the buffer will always be large enough for at least the constant section to be verified @@ -638,26 +642,25 @@ public static bool TryReadBlock(ReadOnlySpan buffer, Stream furtherReads, return false; } - header.VersionMadeBySpecification = buffer[FieldLocations.VersionMadeBySpecification]; - header.VersionMadeByCompatibility = buffer[FieldLocations.VersionMadeByCompatibility]; - header.VersionNeededToExtract = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.VersionNeededToExtract..]); - header.GeneralPurposeBitFlag = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.GeneralPurposeBitFlags..]); - header.CompressionMethod = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.CompressionMethod..]); - header.LastModified = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.LastModified..]); - header.Crc32 = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.Crc32..]); + header = new() + { + VersionMadeBySpecification = buffer[FieldLocations.VersionMadeBySpecification], + VersionMadeByCompatibility = buffer[FieldLocations.VersionMadeByCompatibility], + VersionNeededToExtract = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.VersionNeededToExtract..]), + GeneralPurposeBitFlag = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.GeneralPurposeBitFlags..]), + CompressionMethod = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.CompressionMethod..]), + LastModified = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.LastModified..]), + Crc32 = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.Crc32..]), + FilenameLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.FilenameLength..]), + ExtraFieldLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.ExtraFieldLength..]), + FileCommentLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.FileCommentLength..]), + InternalFileAttributes = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.InternalFileAttributes..]), + ExternalFileAttributes = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.ExternalFileAttributes..]) + }; uint compressedSizeSmall = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.CompressedSize..]); uint uncompressedSizeSmall = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.UncompressedSize..]); - - header.FilenameLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.FilenameLength..]); - header.ExtraFieldLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.ExtraFieldLength..]); - header.FileCommentLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.FileCommentLength..]); - ushort diskNumberStartSmall = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.DiskNumberStart..]); - - header.InternalFileAttributes = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.InternalFileAttributes..]); - header.ExternalFileAttributes = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.ExternalFileAttributes..]); - uint relativeOffsetOfLocalHeaderSmall = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.RelativeOffsetOfLocalHeader..]); // Assemble the dynamic header in a separate buffer. We can't guarantee that it's all in the input buffer, @@ -706,7 +709,7 @@ public static bool TryReadBlock(ReadOnlySpan buffer, Stream furtherReads, ReadOnlySpan zipExtraFields = dynamicHeader.Slice(header.FilenameLength, header.ExtraFieldLength); - zip64 = default; + zip64 = new(); if (saveExtraFieldsAndComments) { header.ExtraFields = ZipGenericExtraField.ParseExtraField(zipExtraFields); @@ -743,7 +746,7 @@ public static bool TryReadBlock(ReadOnlySpan buffer, Stream furtherReads, } } - internal partial struct ZipEndOfCentralDirectoryBlock + internal sealed partial class ZipEndOfCentralDirectoryBlock { // The Zip File Format Specification references 0x06054B50, this is a big endian representation. // ZIP files store values in little endian, so this is reversed. @@ -766,7 +769,8 @@ internal partial struct ZipEndOfCentralDirectoryBlock public ushort NumberOfEntriesInTheCentralDirectory; public uint SizeOfCentralDirectory; public uint OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; - public byte[] ArchiveComment; + private byte[]? _archiveComment; + public byte[] ArchiveComment => _archiveComment ??= []; public static void WriteBlock(Stream stream, long numberOfEntries, long startOfCentralDirectory, long sizeOfCentralDirectory, byte[] archiveComment) { @@ -809,7 +813,7 @@ public static bool TryReadBlock(Stream stream, out ZipEndOfCentralDirectoryBlock Span blockContents = stackalloc byte[TotalSize]; int bytesRead; - eocdBlock = default; + eocdBlock = new(); bytesRead = stream.Read(blockContents); if (bytesRead < TotalSize) @@ -840,12 +844,12 @@ public static bool TryReadBlock(Stream stream, out ZipEndOfCentralDirectoryBlock if (commentLength == 0) { - eocdBlock.ArchiveComment = Array.Empty(); + eocdBlock._archiveComment = []; } else { - eocdBlock.ArchiveComment = new byte[commentLength]; - stream.ReadExactly(eocdBlock.ArchiveComment); + eocdBlock._archiveComment = new byte[commentLength]; + stream.ReadExactly(eocdBlock._archiveComment); } return true;