mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-24 16:05:27 +00:00
Merge branch 'master' into async
# Conflicts: # src/SharpCompress/Archives/GZip/GZipArchive.cs # src/SharpCompress/Common/GZip/GZipFilePart.cs # src/SharpCompress/Common/Tar/Headers/TarHeader.cs # src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs # src/SharpCompress/Common/Zip/ZipFilePart.cs # src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs # src/SharpCompress/Compressors/LZMA/LZipStream.cs # src/SharpCompress/Compressors/Xz/BinaryUtils.cs # src/SharpCompress/Compressors/Xz/Crc32.cs # src/SharpCompress/Writers/Tar/TarWriter.cs # src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs # src/SharpCompress/Writers/Zip/ZipWriter.cs
This commit is contained in:
@@ -105,14 +105,15 @@ namespace SharpCompress.Archives.GZip
|
||||
{
|
||||
// read the header on the first read
|
||||
using var header = MemoryPool<byte>.Shared.Rent(10);
|
||||
var slice = header.Memory.Slice(0, 10);
|
||||
|
||||
// workitem 8501: handle edge case (decompress empty stream)
|
||||
if (!await stream.ReadFullyAsync(header.Memory.Slice(0, 10), cancellationToken))
|
||||
if (!await stream.ReadFullyAsync(, cancellationToken))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.Memory.Span[0] != 0x1F || header.Memory.Span[1] != 0x8B || header.Memory.Span[2] != 8)
|
||||
if (slice.Span[0] != 0x1F || slice.Span[1] != 0x8B || slice.Span[2] != 8)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ using SharpCompress.Readers.Rar;
|
||||
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
public class
|
||||
RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
{
|
||||
internal Lazy<IRarUnpack> UnpackV2017 { get; } = new Lazy<IRarUnpack>(() => new SharpCompress.Compressors.Rar.UnpackV2017.Unpack());
|
||||
internal Lazy<IRarUnpack> UnpackV1 { get; } = new Lazy<IRarUnpack>(() => new SharpCompress.Compressors.Rar.UnpackV1.Unpack());
|
||||
@@ -42,7 +43,7 @@ namespace SharpCompress.Archives.Rar
|
||||
|
||||
protected override IEnumerable<RarArchiveEntry> LoadEntries(IEnumerable<RarVolume> volumes)
|
||||
{
|
||||
return RarArchiveEntryFactory.GetEntries(this, volumes);
|
||||
return RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
|
||||
}
|
||||
|
||||
protected override IEnumerable<RarVolume> LoadVolumes(IEnumerable<Stream> streams)
|
||||
|
||||
@@ -6,6 +6,7 @@ using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.Compressors.Rar;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
@@ -13,11 +14,13 @@ namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
private readonly ICollection<RarFilePart> parts;
|
||||
private readonly RarArchive archive;
|
||||
private readonly ReaderOptions readerOptions;
|
||||
|
||||
internal RarArchiveEntry(RarArchive archive, IEnumerable<RarFilePart> parts)
|
||||
internal RarArchiveEntry(RarArchive archive, IEnumerable<RarFilePart> parts, ReaderOptions readerOptions)
|
||||
{
|
||||
this.parts = parts.ToList();
|
||||
this.archive = archive;
|
||||
this.readerOptions = readerOptions;
|
||||
}
|
||||
|
||||
public override CompressionType CompressionType => CompressionType.Rar;
|
||||
@@ -69,13 +72,14 @@ namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
get
|
||||
{
|
||||
return parts.Select(fp => fp.FileHeader).Any(fh => !fh.IsSplitAfter);
|
||||
var headers = parts.Select(x => x.FileHeader);
|
||||
return !headers.First().IsSplitBefore && !headers.Last().IsSplitAfter;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckIncomplete()
|
||||
{
|
||||
if (!IsComplete)
|
||||
if (!readerOptions.DisableCheckIncomplete && !IsComplete)
|
||||
{
|
||||
throw new IncompleteArchiveException("ArchiveEntry is incomplete and cannot perform this operation.");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
@@ -36,11 +37,12 @@ namespace SharpCompress.Archives.Rar
|
||||
}
|
||||
|
||||
internal static IEnumerable<RarArchiveEntry> GetEntries(RarArchive archive,
|
||||
IEnumerable<RarVolume> rarParts)
|
||||
IEnumerable<RarVolume> rarParts,
|
||||
ReaderOptions readerOptions)
|
||||
{
|
||||
foreach (var groupedParts in GetMatchedFileParts(rarParts))
|
||||
{
|
||||
yield return new RarArchiveEntry(archive, groupedParts);
|
||||
yield return new RarArchiveEntry(archive, groupedParts, readerOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,6 +437,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
internal long DataStartPosition { get; set; }
|
||||
public Stream PackedStream { get; set; }
|
||||
|
||||
public bool IsSplitBefore => IsRar5 ? HasHeaderFlag(HeaderFlagsV5.SPLIT_BEFORE) : HasFlag(FileFlagsV4.SPLIT_BEFORE);
|
||||
public bool IsSplitAfter => IsRar5 ? HasHeaderFlag(HeaderFlagsV5.SPLIT_AFTER) : HasFlag(FileFlagsV4.SPLIT_AFTER);
|
||||
|
||||
public bool IsDirectory => HasFlag(IsRar5 ? FileFlagsV5.DIRECTORY : FileFlagsV4.DIRECTORY);
|
||||
|
||||
@@ -50,11 +50,11 @@ namespace SharpCompress.Common.Rar
|
||||
if (sizeToRead > 0)
|
||||
{
|
||||
int alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf);
|
||||
byte[] cipherText = new byte[RarRijndael.CRYPTO_BLOCK_SIZE];
|
||||
Span<byte> cipherText = stackalloc byte[RarRijndael.CRYPTO_BLOCK_SIZE];
|
||||
for (int i = 0; i < alignedSize / 16; i++)
|
||||
{
|
||||
//long ax = System.currentTimeMillis();
|
||||
_actualStream.Read(cipherText, 0, RarRijndael.CRYPTO_BLOCK_SIZE);
|
||||
_actualStream.Read(cipherText);
|
||||
|
||||
var readBytes = _rijndael.ProcessBlock(cipherText);
|
||||
foreach (var readByte in readBytes)
|
||||
|
||||
@@ -1519,6 +1519,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
}
|
||||
}
|
||||
|
||||
byte[] buffer = null;
|
||||
foreach (CExtractFolderInfo efi in extractFolderInfoVector)
|
||||
{
|
||||
int startIndex;
|
||||
@@ -1555,7 +1556,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
|
||||
Stream s = DecoderStreamHelper.CreateDecoderStream(_stream, folderStartPackPos, packSizes,
|
||||
folderInfo, db.PasswordProvider);
|
||||
byte[] buffer = new byte[4 << 10];
|
||||
buffer ??= new byte[4 << 10];
|
||||
for (; ; )
|
||||
{
|
||||
int processed = s.Read(buffer, 0, buffer.Length);
|
||||
|
||||
@@ -69,53 +69,37 @@ namespace SharpCompress.Common.Zip.Headers
|
||||
Process();
|
||||
}
|
||||
|
||||
//From the spec values are only in the extradata if the standard
|
||||
//value is set to 0xFFFF, but if one of the sizes are present, both are.
|
||||
//Hence if length == 4 volume only
|
||||
// if length == 8 offset only
|
||||
// if length == 12 offset + volume
|
||||
// if length == 16 sizes only
|
||||
// if length == 20 sizes + volume
|
||||
// if length == 24 sizes + offset
|
||||
// if length == 28 everything.
|
||||
//It is unclear how many of these are used in the wild.
|
||||
|
||||
private void Process()
|
||||
{
|
||||
if (DataBytes.Length >= 8)
|
||||
{
|
||||
UncompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes);
|
||||
}
|
||||
|
||||
if (DataBytes.Length >= 16)
|
||||
{
|
||||
CompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(8));
|
||||
}
|
||||
|
||||
if (DataBytes.Length >= 24)
|
||||
{
|
||||
RelativeOffsetOfEntryHeader = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(16));
|
||||
}
|
||||
|
||||
if (DataBytes.Length >= 28)
|
||||
{
|
||||
VolumeNumber = BinaryPrimitives.ReadUInt32LittleEndian(DataBytes.AsSpan(24));
|
||||
}
|
||||
|
||||
switch (DataBytes.Length)
|
||||
{
|
||||
case 4:
|
||||
VolumeNumber = BinaryPrimitives.ReadUInt32LittleEndian(DataBytes);
|
||||
return;
|
||||
case 8:
|
||||
RelativeOffsetOfEntryHeader = BinaryPrimitives.ReadInt64LittleEndian(DataBytes);
|
||||
return;
|
||||
case 12:
|
||||
RelativeOffsetOfEntryHeader = BinaryPrimitives.ReadInt64LittleEndian(DataBytes);
|
||||
VolumeNumber = BinaryPrimitives.ReadUInt32LittleEndian(DataBytes.AsSpan(8));
|
||||
return;
|
||||
case 16:
|
||||
UncompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes);
|
||||
CompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(8));
|
||||
return;
|
||||
case 20:
|
||||
UncompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes);
|
||||
CompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(8));
|
||||
VolumeNumber = BinaryPrimitives.ReadUInt32LittleEndian(DataBytes.AsSpan(16));
|
||||
return;
|
||||
case 24:
|
||||
UncompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes);
|
||||
CompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(8));
|
||||
RelativeOffsetOfEntryHeader = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(16));
|
||||
return;
|
||||
case 28:
|
||||
UncompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes);
|
||||
CompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(8));
|
||||
RelativeOffsetOfEntryHeader = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(16));
|
||||
VolumeNumber = BinaryPrimitives.ReadUInt32LittleEndian(DataBytes.AsSpan(24));
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
throw new ArchiveException("Unexpected size of of Zip64 extended information extra field");
|
||||
throw new ArchiveException($"Unexpected size of of Zip64 extended information extra field: {DataBytes.Length}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@ namespace SharpCompress.Common.Zip
|
||||
{
|
||||
internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory
|
||||
{
|
||||
private const int MAX_ITERATIONS_FOR_DIRECTORY_HEADER = 4096;
|
||||
private const int MINIMUM_EOCD_LENGTH = 22;
|
||||
private const int ZIP64_EOCD_LENGTH = 20;
|
||||
// Comment may be within 64kb + structure 22 bytes
|
||||
private const int MAX_SEARCH_LENGTH_FOR_EOCD = 65557;
|
||||
private bool _zip64;
|
||||
|
||||
internal SeekableZipHeaderFactory(string? password, ArchiveEncoding archiveEncoding)
|
||||
@@ -22,14 +25,26 @@ namespace SharpCompress.Common.Zip
|
||||
|
||||
internal async IAsyncEnumerable<ZipHeader> ReadSeekableHeader(Stream stream, [EnumeratorCancellation]CancellationToken cancellationToken)
|
||||
{
|
||||
await SeekBackToHeader(stream, DIRECTORY_END_HEADER_BYTES, cancellationToken);
|
||||
var reader = new BinaryReader(stream);
|
||||
|
||||
SeekBackToHeader(stream, reader);
|
||||
|
||||
var eocd_location = stream.Position;
|
||||
var entry = new DirectoryEndHeader();
|
||||
await entry.Read(stream, cancellationToken);
|
||||
|
||||
if (entry.IsZip64)
|
||||
{
|
||||
_zip64 = true;
|
||||
await SeekBackToHeader(stream, ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR, cancellationToken);
|
||||
|
||||
// ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR should be before the EOCD
|
||||
stream.Seek(eocd_location - ZIP64_EOCD_LENGTH - 4, SeekOrigin.Begin);
|
||||
uint zip64_locator = reader.ReadUInt32();
|
||||
if( zip64_locator != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR )
|
||||
{
|
||||
throw new ArchiveException("Failed to locate the Zip64 Directory Locator");
|
||||
}
|
||||
|
||||
var zip64Locator = new Zip64DirectoryEndLocatorHeader();
|
||||
await zip64Locator.Read(stream, cancellationToken);
|
||||
|
||||
@@ -75,27 +90,49 @@ namespace SharpCompress.Common.Zip
|
||||
}
|
||||
}
|
||||
|
||||
private static async ValueTask SeekBackToHeader(Stream stream, uint headerSignature, CancellationToken cancellationToken)
|
||||
private static bool IsMatch( byte[] haystack, int position, byte[] needle)
|
||||
{
|
||||
long offset = 0;
|
||||
uint signature;
|
||||
int iterationCount = 0;
|
||||
do
|
||||
for( int i = 0; i < needle.Length; i++ )
|
||||
{
|
||||
if ((stream.Length + offset) - 4 < 0)
|
||||
if( haystack[ position + i ] != needle[ i ] )
|
||||
{
|
||||
throw new ArchiveException("Failed to locate the Zip Header");
|
||||
}
|
||||
stream.Seek(offset - 4, SeekOrigin.End);
|
||||
signature = await stream.ReadLittleEndianUInt32(cancellationToken);
|
||||
offset--;
|
||||
iterationCount++;
|
||||
if (iterationCount > MAX_ITERATIONS_FOR_DIRECTORY_HEADER)
|
||||
{
|
||||
throw new ArchiveException("Could not find Zip file Directory at the end of the file. File may be corrupted.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (signature != headerSignature);
|
||||
|
||||
return true;
|
||||
}
|
||||
private static void SeekBackToHeader(Stream stream, BinaryReader reader)
|
||||
{
|
||||
// Minimum EOCD length
|
||||
if (stream.Length < MINIMUM_EOCD_LENGTH)
|
||||
{
|
||||
throw new ArchiveException("Could not find Zip file Directory at the end of the file. File may be corrupted.");
|
||||
}
|
||||
|
||||
int len = stream.Length < MAX_SEARCH_LENGTH_FOR_EOCD ? (int)stream.Length : MAX_SEARCH_LENGTH_FOR_EOCD;
|
||||
// We search for marker in reverse to find the first occurance
|
||||
byte[] needle = { 0x06, 0x05, 0x4b, 0x50 };
|
||||
|
||||
stream.Seek(-len, SeekOrigin.End);
|
||||
|
||||
byte[] seek = reader.ReadBytes(len);
|
||||
|
||||
// Search in reverse
|
||||
Array.Reverse(seek);
|
||||
|
||||
var max_search_area = len - MINIMUM_EOCD_LENGTH;
|
||||
|
||||
for( int pos_from_end = 0; pos_from_end < max_search_area; ++pos_from_end)
|
||||
{
|
||||
if( IsMatch(seek, pos_from_end, needle) )
|
||||
{
|
||||
stream.Seek(-pos_from_end, SeekOrigin.End);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArchiveException("Failed to locate the Zip Header");
|
||||
}
|
||||
|
||||
internal async ValueTask<LocalEntryHeader> GetLocalHeader(Stream stream, DirectoryEntryHeader directoryEntryHeader, CancellationToken cancellationToken)
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace SharpCompress.Common.Zip
|
||||
if (disposing)
|
||||
{
|
||||
//read out last 10 auth bytes
|
||||
var ten = new byte[10];
|
||||
Span<byte> ten = stackalloc byte[10];
|
||||
_stream.ReadFully(ten);
|
||||
_stream.Dispose();
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ namespace SharpCompress.Compressors.Deflate
|
||||
if (_z.AvailableBytesIn != 8)
|
||||
{
|
||||
// Make sure we have read to the end of the stream
|
||||
Array.Copy(_z.InputBuffer, _z.NextIn, trailer, 0, _z.AvailableBytesIn);
|
||||
_z.InputBuffer.AsSpan(_z.NextIn, _z.AvailableBytesIn).CopyTo(trailer);
|
||||
int bytesNeeded = 8 - _z.AvailableBytesIn;
|
||||
int bytesRead = await _stream.ReadAsync(trailer,
|
||||
_z.AvailableBytesIn,
|
||||
@@ -272,7 +272,7 @@ namespace SharpCompress.Compressors.Deflate
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(_z.InputBuffer, _z.NextIn, trailer, 0, trailer.Length);
|
||||
_z.InputBuffer.AsSpan(_z.NextIn, trailer.Length).CopyTo(trailer);
|
||||
}
|
||||
|
||||
Int32 crc32_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer);
|
||||
|
||||
@@ -31,7 +31,11 @@ namespace SharpCompress.Compressors.PPMd
|
||||
public PpmdVersion Version { get; } = PpmdVersion.I1;
|
||||
internal ModelRestorationMethod RestorationMethod { get; }
|
||||
|
||||
public PpmdProperties(byte[] properties)
|
||||
public PpmdProperties(byte[] properties) : this(properties.AsSpan())
|
||||
{
|
||||
}
|
||||
|
||||
public PpmdProperties(ReadOnlySpan<byte> properties)
|
||||
{
|
||||
if (properties.Length == 2)
|
||||
{
|
||||
@@ -43,7 +47,7 @@ namespace SharpCompress.Compressors.PPMd
|
||||
else if (properties.Length == 5)
|
||||
{
|
||||
Version = PpmdVersion.H7Z;
|
||||
AllocatorSize = BinaryPrimitives.ReadInt32LittleEndian(properties.AsSpan(1));
|
||||
AllocatorSize = BinaryPrimitives.ReadInt32LittleEndian(properties.Slice(1));
|
||||
ModelOrder = properties[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
@@ -11,7 +12,7 @@ namespace SharpCompress.Compressors.Xz
|
||||
public static int ReadLittleEndianInt32(this BinaryReader reader)
|
||||
{
|
||||
byte[] bytes = reader.ReadBytes(4);
|
||||
return (bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24));
|
||||
return BinaryPrimitives.ReadInt32LittleEndian(bytes);
|
||||
}
|
||||
|
||||
internal static uint ReadLittleEndianUInt32(this BinaryReader reader)
|
||||
@@ -21,12 +22,13 @@ namespace SharpCompress.Compressors.Xz
|
||||
public static async ValueTask<int> ReadLittleEndianInt32(this Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
using var buffer = MemoryPool<byte>.Shared.Rent(4);
|
||||
var read = await stream.ReadAsync(buffer.Memory.Slice(0, 4), cancellationToken);
|
||||
var slice = buffer.Memory.Slice(0, 4);
|
||||
var read = await stream.ReadAsync(slice, cancellationToken);
|
||||
if (read != 4)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
return (buffer.Memory.Span[0] + (buffer.Memory.Span[1] << 8) + (buffer.Memory.Span[2] << 16) + (buffer.Memory.Span[3] << 24));
|
||||
return BinaryPrimitives.ReadInt32LittleEndian(slice.Span);
|
||||
}
|
||||
|
||||
internal static async ValueTask<uint> ReadLittleEndianUInt32(this Stream stream, CancellationToken cancellationToken)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SharpCompress.Compressors.Xz
|
||||
{
|
||||
@@ -24,7 +23,7 @@ namespace SharpCompress.Compressors.Xz
|
||||
|
||||
public static UInt32 Compute(UInt32 polynomial, UInt32 seed, ReadOnlyMemory<byte> buffer)
|
||||
{
|
||||
return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
|
||||
return ~CalculateHash(InitializeTable(polynomial), seed, buffer);
|
||||
}
|
||||
|
||||
private static UInt32[] InitializeTable(UInt32 polynomial)
|
||||
@@ -61,16 +60,16 @@ namespace SharpCompress.Compressors.Xz
|
||||
return createTable;
|
||||
}
|
||||
|
||||
private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, ReadOnlyMemory<byte> buffer, int start, int size)
|
||||
private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
var crc = seed;
|
||||
for (var i = start; i < size - start; i++)
|
||||
int len = buffer.Length;
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
crc = (crc >> 8) ^ table[buffer.Span[i] ^ crc & 0xff];
|
||||
crc = (crc >> 8) ^ table[(buffer[i] ^ crc) & 0xff];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ namespace SharpCompress.Compressors.Xz
|
||||
{
|
||||
Table ??= CreateTable(Iso3309Polynomial);
|
||||
|
||||
return CalculateHash(seed, Table, buffer, 0, buffer.Length);
|
||||
return CalculateHash(seed, Table, buffer);
|
||||
}
|
||||
|
||||
public static UInt64 CalculateHash(UInt64 seed, UInt64[] table, IList<byte> buffer, int start, int size)
|
||||
public static UInt64 CalculateHash(UInt64 seed, UInt64[] table, ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
var crc = seed;
|
||||
|
||||
for (var i = start; i < size; i++)
|
||||
int len = buffer.Length;
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace System.IO
|
||||
|
||||
try
|
||||
{
|
||||
int read = stream.Read(temp, 0, buffer.Length);
|
||||
int read = stream.Read(buffer);
|
||||
|
||||
temp.AsSpan(0, read).CopyTo(buffer);
|
||||
|
||||
@@ -42,4 +42,4 @@ namespace System.IO
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -10,5 +10,7 @@ namespace SharpCompress.Readers
|
||||
public bool LookForHeader { get; set; }
|
||||
|
||||
public string? Password { get; set; }
|
||||
|
||||
public bool DisableCheckIncomplete { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>SharpCompress - Pure C# Decompression/Compression</AssemblyTitle>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<VersionPrefix>0.27.1</VersionPrefix>
|
||||
<AssemblyVersion>0.27.1</AssemblyVersion>
|
||||
<FileVersion>0.27.1</FileVersion>
|
||||
<VersionPrefix>0.28.0</VersionPrefix>
|
||||
<AssemblyVersion>0.28.0</AssemblyVersion>
|
||||
<FileVersion>0.28.0</FileVersion>
|
||||
<Authors>Adam Hathcock</Authors>
|
||||
<TargetFrameworks>netstandard2.1;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
|
||||
@@ -378,11 +378,11 @@ namespace SharpCompress
|
||||
return ArrayPool<byte>.Shared.Rent(81920);
|
||||
}
|
||||
|
||||
public static bool ReadFully(this Stream stream, byte[] buffer)
|
||||
public static bool ReadFully(this Stream stream, Span<byte> buffer)
|
||||
{
|
||||
int total = 0;
|
||||
int read;
|
||||
while ((read = stream.Read(buffer, total, buffer.Length - total)) > 0)
|
||||
while ((read = stream.Read(buffer.Slice(total, buffer.Length - total))) > 0)
|
||||
{
|
||||
total += read;
|
||||
if (total >= buffer.Length)
|
||||
|
||||
@@ -136,4 +136,4 @@ namespace SharpCompress.Writers.Tar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,10 +163,10 @@ namespace SharpCompress.Writers.Zip
|
||||
var explicitZipCompressionInfo = ToZipCompressionMethod(zipWriterEntryOptions.CompressionType ?? compressionType);
|
||||
byte[] encodedFilename = WriterOptions.ArchiveEncoding.Encode(filename);
|
||||
|
||||
// TODO: Use stackalloc when we exclusively support netstandard2.1 or higher
|
||||
using var intBuf = MemoryPool<byte>.Shared.Rent(4);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, ZipHeaderFactory.ENTRY_HEADER_BYTES);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,4), cancellationToken);
|
||||
using var buffer = MemoryPool<byte>.Shared.Rent(4);
|
||||
var intBuf = buffer.Memory.Slice(0, 4);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Span, ZipHeaderFactory.ENTRY_HEADER_BYTES);
|
||||
await OutputStream.WriteAsync(intBuf, cancellationToken);
|
||||
if (explicitZipCompressionInfo == ZipCompressionMethod.Deflate)
|
||||
{
|
||||
if (OutputStream.CanSeek && useZip64)
|
||||
@@ -202,12 +202,12 @@ namespace SharpCompress.Writers.Zip
|
||||
}
|
||||
}
|
||||
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)flags);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,2), cancellationToken);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)explicitZipCompressionInfo);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,2), cancellationToken); // zipping method
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, zipWriterEntryOptions.ModificationDateTime.DateTimeToDosTime());
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,4), cancellationToken);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Span, (ushort)flags);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0,2), cancellationToken);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Span, (ushort)explicitZipCompressionInfo);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0,2), cancellationToken); // zipping method
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Span, zipWriterEntryOptions.ModificationDateTime.DateTimeToDosTime());
|
||||
await OutputStream.WriteAsync(intBuf, cancellationToken);
|
||||
|
||||
// zipping date and time
|
||||
using var buf2 = MemoryPool<byte>.Shared.Rent(12);
|
||||
@@ -215,8 +215,8 @@ namespace SharpCompress.Writers.Zip
|
||||
await OutputStream.WriteAsync(buf2.Memory.Slice(0, 12), cancellationToken);
|
||||
|
||||
// unused CRC, un/compressed size, updated later
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)encodedFilename.Length);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,2), cancellationToken);// filename length
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Span, (ushort)encodedFilename.Length);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0,2), cancellationToken);// filename length
|
||||
|
||||
var extralength = 0;
|
||||
if (OutputStream.CanSeek && useZip64)
|
||||
@@ -224,8 +224,8 @@ namespace SharpCompress.Writers.Zip
|
||||
extralength = 2 + 2 + 8 + 8;
|
||||
}
|
||||
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)extralength);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,2), cancellationToken);// extra length
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Span, (ushort)extralength);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0,2), cancellationToken);// extra length
|
||||
await OutputStream.WriteAsync(encodedFilename.AsMemory(), cancellationToken);
|
||||
|
||||
if (extralength != 0)
|
||||
@@ -241,13 +241,14 @@ namespace SharpCompress.Writers.Zip
|
||||
|
||||
private async Task WriteFooterAsync(uint crc, uint compressed, uint uncompressed)
|
||||
{
|
||||
using var intBuf = MemoryPool<byte>.Shared.Rent(4);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, crc);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4));
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, compressed);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4));
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, uncompressed);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4));
|
||||
using var buffer = MemoryPool<byte>.Shared.Rent(4);
|
||||
var intBuf = buffer.Memory.Slice(0, 4);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Span, crc);
|
||||
await OutputStream.WriteAsync(intBuf);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Span, compressed);
|
||||
await OutputStream.WriteAsync(intBuf);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Span, uncompressed);
|
||||
await OutputStream.WriteAsync(intBuf);
|
||||
}
|
||||
|
||||
private async Task WriteEndRecordAsync(ulong size)
|
||||
@@ -258,76 +259,76 @@ namespace SharpCompress.Writers.Zip
|
||||
var sizevalue = size >= uint.MaxValue ? uint.MaxValue : (uint)size;
|
||||
var streampositionvalue = streamPosition >= uint.MaxValue ? uint.MaxValue : (uint)streamPosition;
|
||||
|
||||
using var intBuf = MemoryPool<byte>.Shared.Rent(8);
|
||||
using var buffer = MemoryPool<byte>.Shared.Rent(8);
|
||||
var intBuf = buffer.Memory.Slice(0, 8);
|
||||
if (zip64)
|
||||
{
|
||||
var recordlen = 2 + 2 + 4 + 4 + 8 + 8 + 8 + 8;
|
||||
|
||||
// Write zip64 end of central directory record
|
||||
var s = intBuf.Memory.Slice(0, 4);
|
||||
var s = intBuf.Slice(0, 4);
|
||||
s.Span[0] = 80;
|
||||
s.Span[1] = 75;
|
||||
s.Span[2] = 6;
|
||||
s.Span[3] = 6;
|
||||
await OutputStream.WriteAsync(s);
|
||||
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Memory.Span, (ulong)recordlen);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,8));// Size of zip64 end of central directory record
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, 0);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 2)); // Made by
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, 45);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 2)); // Version needed
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Span, (ulong)recordlen);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0,8));// Size of zip64 end of central directory record
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Span, 45);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0, 2)); // Made by
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Span, 45);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0, 2)); // Version needed
|
||||
|
||||
intBuf.Memory.Span.Clear();
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4)); // Disk number
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4)); // Central dir disk
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Span, 0);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0, 4)); // Disk number
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0, 4)); // Central dir disk
|
||||
|
||||
// TODO: entries.Count is int, so max 2^31 files
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Memory.Span, (ulong)entries.Count);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,8)); // Entries in this disk
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,8)); // Total entries
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Memory.Span, size);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,8)); // Central Directory size
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Memory.Span, (ulong)streamPosition);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,8)); // Disk offset
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Span, (ulong)entries.Count);
|
||||
await OutputStream.WriteAsync(intBuf); // Entries in this disk
|
||||
await OutputStream.WriteAsync(intBuf); // Total entries
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Span, size);
|
||||
await OutputStream.WriteAsync(intBuf); // Central Directory size
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Span, (ulong)streamPosition);
|
||||
await OutputStream.WriteAsync(intBuf); // Disk offset
|
||||
|
||||
// Write zip64 end of central directory locator
|
||||
s = intBuf.Memory.Slice(0, 4);
|
||||
s = intBuf.Slice(0, 4);
|
||||
s.Span[0] = 80;
|
||||
s.Span[1] = 75;
|
||||
s.Span[2] = 6;
|
||||
s.Span[3] = 7;
|
||||
await OutputStream.WriteAsync(s);
|
||||
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, 0);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4)); // Entry disk
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Memory.Span, (ulong)streamPosition + size);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,8)); // Offset to the zip64 central directory
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, 0);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4)); // Number of disks
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Span, 0);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0, 4)); // Entry disk
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Span, (ulong)streamPosition + size);
|
||||
await OutputStream.WriteAsync(intBuf); // Offset to the zip64 central directory
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Span, 1);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0, 4)); // Number of disks
|
||||
|
||||
streamPosition += recordlen + (4 + 4 + 8 + 4);
|
||||
streampositionvalue = streamPosition >= uint.MaxValue ? uint.MaxValue : (uint)streampositionvalue;
|
||||
}
|
||||
|
||||
// Write normal end of central directory record
|
||||
intBuf.Memory.Span.Clear();
|
||||
var x = intBuf.Memory.Slice(0, 4);
|
||||
x.Span[0] = 80;
|
||||
x.Span[1] = 75;
|
||||
x.Span[2] = 5;
|
||||
x.Span[3] = 6;
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0,8));
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)entries.Count);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 2));
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 2));//TODO: this is twice?
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, sizevalue);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4));
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, streampositionvalue);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4));
|
||||
intBuf.Span.Clear();
|
||||
intBuf.Span[0] = 80;
|
||||
intBuf.Span[1] = 75;
|
||||
intBuf.Span[2] = 5;
|
||||
intBuf.Span[3] = 6;
|
||||
await OutputStream.WriteAsync(intBuf);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Span, (ushort)entries.Count);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0, 2));
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0, 2));//TODO: this is twice?
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Span, sizevalue);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0, 4));
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Span, streampositionvalue);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0, 4));
|
||||
byte[] encodedComment = WriterOptions.ArchiveEncoding.Encode(zipComment);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)encodedComment.Length);
|
||||
await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 2));
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Span, (ushort)encodedComment.Length);
|
||||
await OutputStream.WriteAsync(intBuf.Slice(0, 2));
|
||||
await OutputStream.WriteAsync(encodedComment.AsMemory());
|
||||
}
|
||||
|
||||
@@ -514,9 +515,10 @@ namespace SharpCompress.Writers.Zip
|
||||
throw new NotSupportedException("Streams larger than 4GiB are not supported for non-seekable streams");
|
||||
}
|
||||
|
||||
using var intBuf = MemoryPool<byte>.Shared.Rent(4);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, ZipHeaderFactory.POST_DATA_DESCRIPTOR);
|
||||
await originalStream.WriteAsync(intBuf.Memory.Slice(0,4));
|
||||
using var buffer = MemoryPool<byte>.Shared.Rent(4);
|
||||
var intBuf = buffer.Memory.Slice(0, 4);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Span, ZipHeaderFactory.POST_DATA_DESCRIPTOR);
|
||||
await originalStream.WriteAsync(intBuf);
|
||||
await writer.WriteFooterAsync(entry.Crc,
|
||||
compressedvalue,
|
||||
decompressedvalue);
|
||||
|
||||
@@ -18,8 +18,8 @@ namespace SharpCompress.Test.GZip
|
||||
{
|
||||
await ReadAsync("Tar.tar.gz", CompressionType.GZip);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public async ValueTask GZip_Reader_Generic2()
|
||||
{
|
||||
|
||||
@@ -558,5 +558,17 @@ namespace SharpCompress.Test.Zip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Zip_LongComment_Read()
|
||||
{
|
||||
string zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.LongComment.zip");
|
||||
|
||||
using(ZipArchive za = ZipArchive.Open(zipPath))
|
||||
{
|
||||
var count = za.Entries.Count;
|
||||
Assert.Equal(1, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
tests/TestArchives/Archives/Zip.LongComment.zip
Normal file
BIN
tests/TestArchives/Archives/Zip.LongComment.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user