mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-04 05:25:00 +00:00
move more and fmt
This commit is contained in:
@@ -17,7 +17,6 @@ public abstract partial class AbstractArchive<TEntry, TVolume>
|
||||
|
||||
public IAsyncEnumerable<TVolume> VolumesAsync => _lazyVolumesAsync;
|
||||
|
||||
|
||||
protected virtual async IAsyncEnumerable<TEntry> LoadEntriesAsync(
|
||||
IAsyncEnumerable<TVolume> volumes
|
||||
)
|
||||
|
||||
@@ -81,7 +81,6 @@ public abstract partial class AbstractArchive<TEntry, TVolume> : IArchive, IAsyn
|
||||
protected virtual IAsyncEnumerable<TVolume> LoadVolumesAsync(SourceStream sourceStream) =>
|
||||
LoadVolumes(sourceStream).ToAsyncEnumerable();
|
||||
|
||||
|
||||
IEnumerable<IArchiveEntry> IArchive.Entries => Entries.Cast<IArchiveEntry>();
|
||||
|
||||
IEnumerable<IVolume> IArchive.Volumes => _lazyVolumes.Cast<IVolume>();
|
||||
|
||||
157
src/SharpCompress/Archives/ArchiveFactory.Async.cs
Normal file
157
src/SharpCompress/Archives/ArchiveFactory.Async.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Factories;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Archives;
|
||||
|
||||
public static partial class ArchiveFactory
|
||||
{
|
||||
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
readerOptions ??= new ReaderOptions();
|
||||
stream = SharpCompressStream.Create(stream, bufferSize: readerOptions.BufferSize);
|
||||
var factory = await FindFactoryAsync<IArchiveFactory>(stream, cancellationToken);
|
||||
return factory.OpenAsyncArchive(stream, readerOptions);
|
||||
}
|
||||
|
||||
public static ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
string filePath,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
filePath.NotNullOrEmpty(nameof(filePath));
|
||||
return OpenAsyncArchive(new FileInfo(filePath), options, cancellationToken);
|
||||
}
|
||||
|
||||
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
|
||||
var factory = await FindFactoryAsync<IArchiveFactory>(fileInfo, cancellationToken);
|
||||
return factory.OpenAsyncArchive(fileInfo, options);
|
||||
}
|
||||
|
||||
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
IEnumerable<FileInfo> fileInfos,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
fileInfos.NotNull(nameof(fileInfos));
|
||||
var filesArray = fileInfos.ToArray();
|
||||
if (filesArray.Length == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No files to open");
|
||||
}
|
||||
|
||||
var fileInfo = filesArray[0];
|
||||
if (filesArray.Length == 1)
|
||||
{
|
||||
return await OpenAsyncArchive(fileInfo, options, cancellationToken);
|
||||
}
|
||||
|
||||
fileInfo.NotNull(nameof(fileInfo));
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
|
||||
var factory = await FindFactoryAsync<IMultiArchiveFactory>(fileInfo, cancellationToken);
|
||||
return factory.OpenAsyncArchive(filesArray, options, cancellationToken);
|
||||
}
|
||||
|
||||
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
IEnumerable<Stream> streams,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
streams.NotNull(nameof(streams));
|
||||
var streamsArray = streams.ToArray();
|
||||
if (streamsArray.Length == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No streams");
|
||||
}
|
||||
|
||||
var firstStream = streamsArray[0];
|
||||
if (streamsArray.Length == 1)
|
||||
{
|
||||
return await OpenAsyncArchive(firstStream, options, cancellationToken);
|
||||
}
|
||||
|
||||
firstStream.NotNull(nameof(firstStream));
|
||||
options ??= new ReaderOptions();
|
||||
|
||||
var factory = await FindFactoryAsync<IMultiArchiveFactory>(firstStream, cancellationToken);
|
||||
return factory.OpenAsyncArchive(streamsArray, options);
|
||||
}
|
||||
|
||||
public static ValueTask<T> FindFactoryAsync<T>(
|
||||
string path,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
where T : IFactory
|
||||
{
|
||||
path.NotNullOrEmpty(nameof(path));
|
||||
return FindFactoryAsync<T>(new FileInfo(path), cancellationToken);
|
||||
}
|
||||
|
||||
private static async ValueTask<T> FindFactoryAsync<T>(
|
||||
FileInfo finfo,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
where T : IFactory
|
||||
{
|
||||
finfo.NotNull(nameof(finfo));
|
||||
using Stream stream = finfo.OpenRead();
|
||||
return await FindFactoryAsync<T>(stream, cancellationToken);
|
||||
}
|
||||
|
||||
private static async ValueTask<T> FindFactoryAsync<T>(
|
||||
Stream stream,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
where T : IFactory
|
||||
{
|
||||
stream.NotNull(nameof(stream));
|
||||
if (!stream.CanRead || !stream.CanSeek)
|
||||
{
|
||||
throw new ArgumentException("Stream should be readable and seekable");
|
||||
}
|
||||
|
||||
var factories = Factory.Factories.OfType<T>();
|
||||
|
||||
var startPosition = stream.Position;
|
||||
|
||||
foreach (var factory in factories)
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
if (await factory.IsArchiveAsync(stream, cancellationToken: cancellationToken))
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
||||
var extensions = string.Join(", ", factories.Select(item => item.Name));
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Cannot determine compressed stream type. Supported Archive Formats: {extensions}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archives;
|
||||
|
||||
public static class ArchiveFactory
|
||||
public static partial class ArchiveFactory
|
||||
{
|
||||
public static IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
@@ -20,18 +20,6 @@ public static class ArchiveFactory
|
||||
return FindFactory<IArchiveFactory>(stream).OpenArchive(stream, readerOptions);
|
||||
}
|
||||
|
||||
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
readerOptions ??= new ReaderOptions();
|
||||
stream = SharpCompressStream.Create(stream, bufferSize: readerOptions.BufferSize);
|
||||
var factory = await FindFactoryAsync<IArchiveFactory>(stream, cancellationToken);
|
||||
return factory.OpenAsyncArchive(stream, readerOptions);
|
||||
}
|
||||
|
||||
public static IWritableArchive CreateArchive(ArchiveType type)
|
||||
{
|
||||
var factory = Factory
|
||||
@@ -52,16 +40,6 @@ public static class ArchiveFactory
|
||||
return OpenArchive(new FileInfo(filePath), options);
|
||||
}
|
||||
|
||||
public static ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
string filePath,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
filePath.NotNullOrEmpty(nameof(filePath));
|
||||
return OpenAsyncArchive(new FileInfo(filePath), options, cancellationToken);
|
||||
}
|
||||
|
||||
public static IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? options = null)
|
||||
{
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
@@ -69,18 +47,6 @@ public static class ArchiveFactory
|
||||
return FindFactory<IArchiveFactory>(fileInfo).OpenArchive(fileInfo, options);
|
||||
}
|
||||
|
||||
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
|
||||
var factory = await FindFactoryAsync<IArchiveFactory>(fileInfo, cancellationToken);
|
||||
return factory.OpenAsyncArchive(fileInfo, options);
|
||||
}
|
||||
|
||||
public static IArchive OpenArchive(
|
||||
IEnumerable<FileInfo> fileInfos,
|
||||
ReaderOptions? options = null
|
||||
@@ -105,32 +71,6 @@ public static class ArchiveFactory
|
||||
return FindFactory<IMultiArchiveFactory>(fileInfo).OpenArchive(filesArray, options);
|
||||
}
|
||||
|
||||
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
IEnumerable<FileInfo> fileInfos,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
fileInfos.NotNull(nameof(fileInfos));
|
||||
var filesArray = fileInfos.ToArray();
|
||||
if (filesArray.Length == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No files to open");
|
||||
}
|
||||
|
||||
var fileInfo = filesArray[0];
|
||||
if (filesArray.Length == 1)
|
||||
{
|
||||
return await OpenAsyncArchive(fileInfo, options, cancellationToken);
|
||||
}
|
||||
|
||||
fileInfo.NotNull(nameof(fileInfo));
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
|
||||
var factory = await FindFactoryAsync<IMultiArchiveFactory>(fileInfo, cancellationToken);
|
||||
return factory.OpenAsyncArchive(filesArray, options, cancellationToken);
|
||||
}
|
||||
|
||||
public static IArchive OpenArchive(IEnumerable<Stream> streams, ReaderOptions? options = null)
|
||||
{
|
||||
streams.NotNull(nameof(streams));
|
||||
@@ -152,33 +92,6 @@ public static class ArchiveFactory
|
||||
return FindFactory<IMultiArchiveFactory>(firstStream).OpenArchive(streamsArray, options);
|
||||
}
|
||||
|
||||
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
IEnumerable<Stream> streams,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
streams.NotNull(nameof(streams));
|
||||
var streamsArray = streams.ToArray();
|
||||
if (streamsArray.Length == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No streams");
|
||||
}
|
||||
|
||||
var firstStream = streamsArray[0];
|
||||
if (streamsArray.Length == 1)
|
||||
{
|
||||
return await OpenAsyncArchive(firstStream, options, cancellationToken);
|
||||
}
|
||||
|
||||
firstStream.NotNull(nameof(firstStream));
|
||||
options ??= new ReaderOptions();
|
||||
|
||||
var factory = await FindFactoryAsync<IMultiArchiveFactory>(firstStream, cancellationToken);
|
||||
return factory.OpenAsyncArchive(streamsArray, options);
|
||||
}
|
||||
|
||||
public static void WriteToDirectory(
|
||||
string sourceArchive,
|
||||
string destinationDirectory,
|
||||
@@ -197,16 +110,6 @@ public static class ArchiveFactory
|
||||
return FindFactory<T>(stream);
|
||||
}
|
||||
|
||||
public static ValueTask<T> FindFactoryAsync<T>(
|
||||
string path,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
where T : IFactory
|
||||
{
|
||||
path.NotNullOrEmpty(nameof(path));
|
||||
return FindFactoryAsync<T>(new FileInfo(path), cancellationToken);
|
||||
}
|
||||
|
||||
public static T FindFactory<T>(FileInfo finfo)
|
||||
where T : IFactory
|
||||
{
|
||||
@@ -247,51 +150,7 @@ public static class ArchiveFactory
|
||||
);
|
||||
}
|
||||
|
||||
private static async ValueTask<T> FindFactoryAsync<T>(
|
||||
FileInfo finfo,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
where T : IFactory
|
||||
{
|
||||
finfo.NotNull(nameof(finfo));
|
||||
using Stream stream = finfo.OpenRead();
|
||||
return await FindFactoryAsync<T>(stream, cancellationToken);
|
||||
}
|
||||
|
||||
private static async ValueTask<T> FindFactoryAsync<T>(
|
||||
Stream stream,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
where T : IFactory
|
||||
{
|
||||
stream.NotNull(nameof(stream));
|
||||
if (!stream.CanRead || !stream.CanSeek)
|
||||
{
|
||||
throw new ArgumentException("Stream should be readable and seekable");
|
||||
}
|
||||
|
||||
var factories = Factory.Factories.OfType<T>();
|
||||
|
||||
var startPosition = stream.Position;
|
||||
|
||||
foreach (var factory in factories)
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
if (await factory.IsArchiveAsync(stream, cancellationToken: cancellationToken))
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
||||
var extensions = string.Join(", ", factories.Select(item => item.Name));
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Cannot determine compressed stream type. Supported Archive Formats: {extensions}"
|
||||
);
|
||||
}
|
||||
// Async methods moved to ArchiveFactory.Async.cs
|
||||
|
||||
public static bool IsArchive(
|
||||
string filePath,
|
||||
|
||||
@@ -88,7 +88,6 @@ public partial class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZi
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
protected override IReader CreateReaderForSolidExtraction()
|
||||
{
|
||||
var stream = Volumes.Single().Stream;
|
||||
|
||||
@@ -73,7 +73,6 @@ public partial class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, Sev
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void LoadFactory(Stream stream)
|
||||
{
|
||||
if (_database is null)
|
||||
|
||||
@@ -88,7 +88,6 @@ public partial class TarArchive
|
||||
return new((IAsyncReader)TarReader.OpenReader(stream));
|
||||
}
|
||||
|
||||
|
||||
protected override async IAsyncEnumerable<TarArchiveEntry> LoadEntriesAsync(
|
||||
IAsyncEnumerable<TarVolume> volumes
|
||||
)
|
||||
|
||||
@@ -91,7 +91,6 @@ public partial class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override TarArchiveEntry CreateEntryInternal(
|
||||
string filePath,
|
||||
Stream source,
|
||||
|
||||
@@ -16,7 +16,6 @@ namespace SharpCompress.Archives.Zip;
|
||||
|
||||
public partial class ZipArchive
|
||||
{
|
||||
|
||||
protected override async IAsyncEnumerable<ZipArchiveEntry> LoadEntriesAsync(
|
||||
IAsyncEnumerable<ZipVolume> volumes
|
||||
)
|
||||
@@ -70,7 +69,6 @@ public partial class ZipArchive
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override async ValueTask SaveToAsync(
|
||||
Stream stream,
|
||||
WriterOptions options,
|
||||
|
||||
15
src/SharpCompress/Common/GZip/GZipEntry.Async.cs
Normal file
15
src/SharpCompress/Common/GZip/GZipEntry.Async.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Common.GZip;
|
||||
|
||||
public partial class GZipEntry
|
||||
{
|
||||
internal static async IAsyncEnumerable<GZipEntry> GetEntriesAsync(
|
||||
Stream stream,
|
||||
OptionsBase options
|
||||
)
|
||||
{
|
||||
yield return new GZipEntry(await GZipFilePart.CreateAsync(stream, options.ArchiveEncoding));
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using System.IO;
|
||||
|
||||
namespace SharpCompress.Common.GZip;
|
||||
|
||||
public class GZipEntry : Entry
|
||||
public partial class GZipEntry : Entry
|
||||
{
|
||||
private readonly GZipFilePart? _filePart;
|
||||
|
||||
@@ -43,11 +43,5 @@ public class GZipEntry : Entry
|
||||
yield return new GZipEntry(GZipFilePart.Create(stream, options.ArchiveEncoding));
|
||||
}
|
||||
|
||||
internal static async IAsyncEnumerable<GZipEntry> GetEntriesAsync(
|
||||
Stream stream,
|
||||
OptionsBase options
|
||||
)
|
||||
{
|
||||
yield return new GZipEntry(await GZipFilePart.CreateAsync(stream, options.ArchiveEncoding));
|
||||
}
|
||||
// Async methods moved to GZipEntry.Async.cs
|
||||
}
|
||||
|
||||
36
src/SharpCompress/Common/Tar/TarEntry.Async.cs
Normal file
36
src/SharpCompress/Common/Tar/TarEntry.Async.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar;
|
||||
|
||||
public partial class TarEntry
|
||||
{
|
||||
internal static async IAsyncEnumerable<TarEntry> GetEntriesAsync(
|
||||
StreamingMode mode,
|
||||
Stream stream,
|
||||
CompressionType compressionType,
|
||||
IArchiveEncoding archiveEncoding
|
||||
)
|
||||
{
|
||||
await foreach (
|
||||
var header in TarHeaderFactory.ReadHeaderAsync(mode, stream, archiveEncoding)
|
||||
)
|
||||
{
|
||||
if (header != null)
|
||||
{
|
||||
if (mode == StreamingMode.Seekable)
|
||||
{
|
||||
yield return new TarEntry(new TarFilePart(header, stream), compressionType);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new TarEntry(new TarFilePart(header, null), compressionType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IncompleteArchiveException("Unexpected EOF reading tar file");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar;
|
||||
|
||||
public class TarEntry : Entry
|
||||
public partial class TarEntry : Entry
|
||||
{
|
||||
private readonly TarFilePart? _filePart;
|
||||
|
||||
@@ -77,32 +77,5 @@ public class TarEntry : Entry
|
||||
}
|
||||
}
|
||||
|
||||
internal static async IAsyncEnumerable<TarEntry> GetEntriesAsync(
|
||||
StreamingMode mode,
|
||||
Stream stream,
|
||||
CompressionType compressionType,
|
||||
IArchiveEncoding archiveEncoding
|
||||
)
|
||||
{
|
||||
await foreach (
|
||||
var header in TarHeaderFactory.ReadHeaderAsync(mode, stream, archiveEncoding)
|
||||
)
|
||||
{
|
||||
if (header != null)
|
||||
{
|
||||
if (mode == StreamingMode.Seekable)
|
||||
{
|
||||
yield return new TarEntry(new TarFilePart(header, stream), compressionType);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new TarEntry(new TarFilePart(header, null), compressionType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IncompleteArchiveException("Unexpected EOF reading tar file");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Async methods moved to TarEntry.Async.cs
|
||||
}
|
||||
|
||||
53
src/SharpCompress/Common/Tar/TarHeaderFactory.Async.cs
Normal file
53
src/SharpCompress/Common/Tar/TarHeaderFactory.Async.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar;
|
||||
|
||||
internal static partial class TarHeaderFactory
|
||||
{
|
||||
internal static async IAsyncEnumerable<TarHeader?> ReadHeaderAsync(
|
||||
StreamingMode mode,
|
||||
Stream stream,
|
||||
IArchiveEncoding archiveEncoding
|
||||
)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
TarHeader? header = null;
|
||||
try
|
||||
{
|
||||
var reader = new AsyncBinaryReader(stream, false);
|
||||
header = new TarHeader(archiveEncoding);
|
||||
if (!await header.ReadAsync(reader))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
switch (mode)
|
||||
{
|
||||
case StreamingMode.Seekable:
|
||||
{
|
||||
header.DataStartPosition = stream.Position;
|
||||
|
||||
//skip to nearest 512
|
||||
stream.Position += PadTo512(header.Size);
|
||||
}
|
||||
break;
|
||||
case StreamingMode.Streaming:
|
||||
{
|
||||
header.PackedStream = new TarReadOnlySubStream(stream, header.Size);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
throw new InvalidFormatException("Invalid StreamingMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
header = null;
|
||||
}
|
||||
yield return header;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar;
|
||||
|
||||
internal static class TarHeaderFactory
|
||||
internal static partial class TarHeaderFactory
|
||||
{
|
||||
internal static IEnumerable<TarHeader?> ReadHeader(
|
||||
StreamingMode mode,
|
||||
@@ -54,51 +54,7 @@ internal static class TarHeaderFactory
|
||||
}
|
||||
}
|
||||
|
||||
internal static async IAsyncEnumerable<TarHeader?> ReadHeaderAsync(
|
||||
StreamingMode mode,
|
||||
Stream stream,
|
||||
IArchiveEncoding archiveEncoding
|
||||
)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
TarHeader? header = null;
|
||||
try
|
||||
{
|
||||
var reader = new AsyncBinaryReader(stream, false);
|
||||
header = new TarHeader(archiveEncoding);
|
||||
if (!await header.ReadAsync(reader))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
switch (mode)
|
||||
{
|
||||
case StreamingMode.Seekable:
|
||||
{
|
||||
header.DataStartPosition = stream.Position;
|
||||
|
||||
//skip to nearest 512
|
||||
stream.Position += PadTo512(header.Size);
|
||||
}
|
||||
break;
|
||||
case StreamingMode.Streaming:
|
||||
{
|
||||
header.PackedStream = new TarReadOnlySubStream(stream, header.Size);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
throw new InvalidFormatException("Invalid StreamingMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
header = null;
|
||||
}
|
||||
yield return header;
|
||||
}
|
||||
}
|
||||
// Async methods moved to TarHeaderFactory.Async.cs
|
||||
|
||||
private static long PadTo512(long size)
|
||||
{
|
||||
|
||||
@@ -75,6 +75,7 @@ internal sealed partial class SeekableZipHeaderFactory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async ValueTask SeekBackToHeaderAsync(Stream stream, AsyncBinaryReader reader)
|
||||
{
|
||||
// Minimum EOCD length
|
||||
|
||||
@@ -22,7 +22,6 @@ internal sealed partial class SeekableZipHeaderFactory : ZipHeaderFactory
|
||||
internal SeekableZipHeaderFactory(string? password, IArchiveEncoding archiveEncoding)
|
||||
: base(StreamingMode.Seekable, password, archiveEncoding) { }
|
||||
|
||||
|
||||
internal IEnumerable<ZipHeader> ReadSeekableHeader(Stream stream)
|
||||
{
|
||||
var reader = new BinaryReader(stream);
|
||||
|
||||
177
src/SharpCompress/Compressors/ADC/ADCBase.Async.cs
Normal file
177
src/SharpCompress/Compressors/ADC/ADCBase.Async.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Compressors.ADC;
|
||||
|
||||
public static partial class ADCBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Decompresses a byte buffer asynchronously that's compressed with ADC
|
||||
/// </summary>
|
||||
/// <param name="input">Compressed buffer</param>
|
||||
/// <param name="bufferSize">Max size for decompressed data</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Result containing bytes read and decompressed data</returns>
|
||||
public static async ValueTask<AdcDecompressResult> DecompressAsync(
|
||||
byte[] input,
|
||||
int bufferSize = 262144,
|
||||
CancellationToken cancellationToken = default
|
||||
) => await DecompressAsync(new MemoryStream(input), bufferSize, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses a stream asynchronously that's compressed with ADC
|
||||
/// </summary>
|
||||
/// <param name="input">Stream containing compressed data</param>
|
||||
/// <param name="bufferSize">Max size for decompressed data</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Result containing bytes read and decompressed data</returns>
|
||||
public static async ValueTask<AdcDecompressResult> DecompressAsync(
|
||||
Stream input,
|
||||
int bufferSize = 262144,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var result = new AdcDecompressResult();
|
||||
|
||||
if (input is null || input.Length == 0)
|
||||
{
|
||||
result.BytesRead = 0;
|
||||
result.Output = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
var start = (int)input.Position;
|
||||
var position = (int)input.Position;
|
||||
int chunkSize;
|
||||
int offset;
|
||||
int chunkType;
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
var outPosition = 0;
|
||||
var full = false;
|
||||
byte[] temp = ArrayPool<byte>.Shared.Rent(3);
|
||||
|
||||
try
|
||||
{
|
||||
while (position < input.Length)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var readByte = input.ReadByte();
|
||||
if (readByte == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
chunkType = GetChunkType((byte)readByte);
|
||||
|
||||
switch (chunkType)
|
||||
{
|
||||
case PLAIN:
|
||||
chunkSize = GetChunkSize((byte)readByte);
|
||||
if (outPosition + chunkSize > bufferSize)
|
||||
{
|
||||
full = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var readCount = await input.ReadAsync(
|
||||
buffer,
|
||||
outPosition,
|
||||
chunkSize,
|
||||
cancellationToken
|
||||
);
|
||||
outPosition += readCount;
|
||||
position += readCount + 1;
|
||||
break;
|
||||
case TWO_BYTE:
|
||||
chunkSize = GetChunkSize((byte)readByte);
|
||||
temp[0] = (byte)readByte;
|
||||
temp[1] = (byte)input.ReadByte();
|
||||
offset = GetOffset(temp.AsSpan(0, 2));
|
||||
if (outPosition + chunkSize > bufferSize)
|
||||
{
|
||||
full = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset == 0)
|
||||
{
|
||||
var lastByte = buffer[outPosition - 1];
|
||||
for (var i = 0; i < chunkSize; i++)
|
||||
{
|
||||
buffer[outPosition] = lastByte;
|
||||
outPosition++;
|
||||
}
|
||||
|
||||
position += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < chunkSize; i++)
|
||||
{
|
||||
buffer[outPosition] = buffer[outPosition - offset - 1];
|
||||
outPosition++;
|
||||
}
|
||||
|
||||
position += 2;
|
||||
}
|
||||
|
||||
break;
|
||||
case THREE_BYTE:
|
||||
chunkSize = GetChunkSize((byte)readByte);
|
||||
temp[0] = (byte)readByte;
|
||||
temp[1] = (byte)input.ReadByte();
|
||||
temp[2] = (byte)input.ReadByte();
|
||||
offset = GetOffset(temp.AsSpan(0, 3));
|
||||
if (outPosition + chunkSize > bufferSize)
|
||||
{
|
||||
full = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset == 0)
|
||||
{
|
||||
var lastByte = buffer[outPosition - 1];
|
||||
for (var i = 0; i < chunkSize; i++)
|
||||
{
|
||||
buffer[outPosition] = lastByte;
|
||||
outPosition++;
|
||||
}
|
||||
|
||||
position += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < chunkSize; i++)
|
||||
{
|
||||
buffer[outPosition] = buffer[outPosition - offset - 1];
|
||||
outPosition++;
|
||||
}
|
||||
|
||||
position += 3;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (full)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var output = new byte[outPosition];
|
||||
Array.Copy(buffer, output, outPosition);
|
||||
result.BytesRead = position - start;
|
||||
result.Output = output;
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
ArrayPool<byte>.Shared.Return(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class AdcDecompressResult
|
||||
/// <summary>
|
||||
/// Provides static methods for decompressing Apple Data Compression data
|
||||
/// </summary>
|
||||
public static class ADCBase
|
||||
public static partial class ADCBase
|
||||
{
|
||||
private const int PLAIN = 1;
|
||||
private const int TWO_BYTE = 2;
|
||||
@@ -97,172 +97,7 @@ public static class ADCBase
|
||||
public static int Decompress(byte[] input, out byte[]? output, int bufferSize = 262144) =>
|
||||
Decompress(new MemoryStream(input), out output, bufferSize);
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses a byte buffer asynchronously that's compressed with ADC
|
||||
/// </summary>
|
||||
/// <param name="input">Compressed buffer</param>
|
||||
/// <param name="bufferSize">Max size for decompressed data</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Result containing bytes read and decompressed data</returns>
|
||||
public static async ValueTask<AdcDecompressResult> DecompressAsync(
|
||||
byte[] input,
|
||||
int bufferSize = 262144,
|
||||
CancellationToken cancellationToken = default
|
||||
) => await DecompressAsync(new MemoryStream(input), bufferSize, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses a stream asynchronously that's compressed with ADC
|
||||
/// </summary>
|
||||
/// <param name="input">Stream containing compressed data</param>
|
||||
/// <param name="bufferSize">Max size for decompressed data</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Result containing bytes read and decompressed data</returns>
|
||||
public static async ValueTask<AdcDecompressResult> DecompressAsync(
|
||||
Stream input,
|
||||
int bufferSize = 262144,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var result = new AdcDecompressResult();
|
||||
|
||||
if (input is null || input.Length == 0)
|
||||
{
|
||||
result.BytesRead = 0;
|
||||
result.Output = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
var start = (int)input.Position;
|
||||
var position = (int)input.Position;
|
||||
int chunkSize;
|
||||
int offset;
|
||||
int chunkType;
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
var outPosition = 0;
|
||||
var full = false;
|
||||
byte[] temp = ArrayPool<byte>.Shared.Rent(3);
|
||||
|
||||
try
|
||||
{
|
||||
while (position < input.Length)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var readByte = input.ReadByte();
|
||||
if (readByte == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
chunkType = GetChunkType((byte)readByte);
|
||||
|
||||
switch (chunkType)
|
||||
{
|
||||
case PLAIN:
|
||||
chunkSize = GetChunkSize((byte)readByte);
|
||||
if (outPosition + chunkSize > bufferSize)
|
||||
{
|
||||
full = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var readCount = await input.ReadAsync(
|
||||
buffer,
|
||||
outPosition,
|
||||
chunkSize,
|
||||
cancellationToken
|
||||
);
|
||||
outPosition += readCount;
|
||||
position += readCount + 1;
|
||||
break;
|
||||
case TWO_BYTE:
|
||||
chunkSize = GetChunkSize((byte)readByte);
|
||||
temp[0] = (byte)readByte;
|
||||
temp[1] = (byte)input.ReadByte();
|
||||
offset = GetOffset(temp.AsSpan(0, 2));
|
||||
if (outPosition + chunkSize > bufferSize)
|
||||
{
|
||||
full = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset == 0)
|
||||
{
|
||||
var lastByte = buffer[outPosition - 1];
|
||||
for (var i = 0; i < chunkSize; i++)
|
||||
{
|
||||
buffer[outPosition] = lastByte;
|
||||
outPosition++;
|
||||
}
|
||||
|
||||
position += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < chunkSize; i++)
|
||||
{
|
||||
buffer[outPosition] = buffer[outPosition - offset - 1];
|
||||
outPosition++;
|
||||
}
|
||||
|
||||
position += 2;
|
||||
}
|
||||
|
||||
break;
|
||||
case THREE_BYTE:
|
||||
chunkSize = GetChunkSize((byte)readByte);
|
||||
temp[0] = (byte)readByte;
|
||||
temp[1] = (byte)input.ReadByte();
|
||||
temp[2] = (byte)input.ReadByte();
|
||||
offset = GetOffset(temp.AsSpan(0, 3));
|
||||
if (outPosition + chunkSize > bufferSize)
|
||||
{
|
||||
full = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset == 0)
|
||||
{
|
||||
var lastByte = buffer[outPosition - 1];
|
||||
for (var i = 0; i < chunkSize; i++)
|
||||
{
|
||||
buffer[outPosition] = lastByte;
|
||||
outPosition++;
|
||||
}
|
||||
|
||||
position += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < chunkSize; i++)
|
||||
{
|
||||
buffer[outPosition] = buffer[outPosition - offset - 1];
|
||||
outPosition++;
|
||||
}
|
||||
|
||||
position += 3;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (full)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var output = new byte[outPosition];
|
||||
Array.Copy(buffer, output, outPosition);
|
||||
result.BytesRead = position - start;
|
||||
result.Output = output;
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
ArrayPool<byte>.Shared.Return(temp);
|
||||
}
|
||||
}
|
||||
// Async methods moved to ADCBase.Async.cs
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses a stream that's compressed with ADC
|
||||
|
||||
@@ -8,6 +8,64 @@ namespace SharpCompress.Compressors.BZip2;
|
||||
|
||||
public sealed partial class BZip2Stream
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a BZip2Stream asynchronously
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="compressionMode">Compression Mode</param>
|
||||
/// <param name="decompressConcatenated">Decompress Concatenated</param>
|
||||
/// <param name="cancellationToken">Cancellation Token</param>
|
||||
public static async ValueTask<BZip2Stream> CreateAsync(
|
||||
Stream stream,
|
||||
CompressionMode compressionMode,
|
||||
bool decompressConcatenated,
|
||||
bool leaveOpen = false,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var bZip2Stream = new BZip2Stream();
|
||||
bZip2Stream.leaveOpen = leaveOpen;
|
||||
#if DEBUG_STREAMS
|
||||
bZip2Stream.DebugConstruct(typeof(BZip2Stream));
|
||||
#endif
|
||||
bZip2Stream.Mode = compressionMode;
|
||||
if (bZip2Stream.Mode == CompressionMode.Compress)
|
||||
{
|
||||
bZip2Stream.stream = new CBZip2OutputStream(stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
bZip2Stream.stream = await CBZip2InputStream.CreateAsync(
|
||||
stream,
|
||||
decompressConcatenated,
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
||||
return bZip2Stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously consumes two bytes to test if there is a BZip2 header
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async ValueTask<bool> IsBZip2Async(
|
||||
Stream stream,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var buffer = new byte[2];
|
||||
var bytesRead = await stream.ReadAsync(buffer, 0, 2, cancellationToken);
|
||||
if (bytesRead < 2 || buffer[0] != 'B' || buffer[1] != 'Z')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET
|
||||
public override async ValueTask<int> ReadAsync(
|
||||
Memory<byte> buffer,
|
||||
|
||||
@@ -69,43 +69,6 @@ public sealed partial class BZip2Stream : Stream, IStreamStack
|
||||
return bZip2Stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a BZip2Stream asynchronously
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="compressionMode">Compression Mode</param>
|
||||
/// <param name="decompressConcatenated">Decompress Concatenated</param>
|
||||
/// <param name="cancellationToken">Cancellation Token</param>
|
||||
public static async ValueTask<BZip2Stream> CreateAsync(
|
||||
Stream stream,
|
||||
CompressionMode compressionMode,
|
||||
bool decompressConcatenated,
|
||||
bool leaveOpen = false,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var bZip2Stream = new BZip2Stream();
|
||||
bZip2Stream.leaveOpen = leaveOpen;
|
||||
#if DEBUG_STREAMS
|
||||
bZip2Stream.DebugConstruct(typeof(BZip2Stream));
|
||||
#endif
|
||||
bZip2Stream.Mode = compressionMode;
|
||||
if (bZip2Stream.Mode == CompressionMode.Compress)
|
||||
{
|
||||
bZip2Stream.stream = new CBZip2OutputStream(stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
bZip2Stream.stream = await CBZip2InputStream.CreateAsync(
|
||||
stream,
|
||||
decompressConcatenated,
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
||||
return bZip2Stream;
|
||||
}
|
||||
|
||||
public void Finish() => (stream as CBZip2OutputStream)?.Finish();
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -178,24 +141,5 @@ public sealed partial class BZip2Stream : Stream, IStreamStack
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously consumes two bytes to test if there is a BZip2 header
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async ValueTask<bool> IsBZip2Async(
|
||||
Stream stream,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var buffer = new byte[2];
|
||||
var bytesRead = await stream.ReadAsync(buffer, 0, 2, cancellationToken);
|
||||
if (bytesRead < 2 || buffer[0] != 'B' || buffer[1] != 'Z')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Async methods moved to BZip2Stream.Async.cs
|
||||
}
|
||||
|
||||
@@ -9,6 +9,57 @@ namespace SharpCompress.Compressors.LZMA;
|
||||
|
||||
public sealed partial class LZipStream
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously determines if the given stream is positioned at the start of a v1 LZip
|
||||
/// file, as indicated by the ASCII characters "LZIP" and a version byte
|
||||
/// of 1, followed by at least one byte.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from. Must not be null.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns><c>true</c> if the given stream is an LZip file, <c>false</c> otherwise.</returns>
|
||||
public static async ValueTask<bool> IsLZipFileAsync(
|
||||
Stream stream,
|
||||
CancellationToken cancellationToken = default
|
||||
) => await ValidateAndReadSizeAsync(stream, cancellationToken) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously reads the 6-byte header of the stream, and returns 0 if either the header
|
||||
/// couldn't be read or it isn't a validate LZIP header, or the dictionary
|
||||
/// size if it *is* a valid LZIP file.
|
||||
/// </summary>
|
||||
public static async ValueTask<int> ValidateAndReadSizeAsync(
|
||||
Stream stream,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
// Read the header
|
||||
byte[] header = new byte[6];
|
||||
var n = await stream
|
||||
.ReadAsync(header, 0, header.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// TODO: Handle reading only part of the header?
|
||||
|
||||
if (n != 6)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (
|
||||
header[0] != 'L'
|
||||
|| header[1] != 'Z'
|
||||
|| header[2] != 'I'
|
||||
|| header[3] != 'P'
|
||||
|| header[4] != 1 /* version 1 */
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var basePower = header[5] & 0x1F;
|
||||
var subtractionNumerator = (header[5] & 0xE0) >> 5;
|
||||
return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4)));
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET
|
||||
/// <summary>
|
||||
/// Asynchronously reads bytes from the current stream into a buffer.
|
||||
|
||||
@@ -202,19 +202,6 @@ public sealed partial class LZipStream : Stream, IStreamStack
|
||||
/// <returns><c>true</c> if the given stream is an LZip file, <c>false</c> otherwise.</returns>
|
||||
public static bool IsLZipFile(Stream stream) => ValidateAndReadSize(stream) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously determines if the given stream is positioned at the start of a v1 LZip
|
||||
/// file, as indicated by the ASCII characters "LZIP" and a version byte
|
||||
/// of 1, followed by at least one byte.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from. Must not be null.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns><c>true</c> if the given stream is an LZip file, <c>false</c> otherwise.</returns>
|
||||
public static async ValueTask<bool> IsLZipFileAsync(
|
||||
Stream stream,
|
||||
CancellationToken cancellationToken = default
|
||||
) => await ValidateAndReadSizeAsync(stream, cancellationToken) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Reads the 6-byte header of the stream, and returns 0 if either the header
|
||||
/// couldn't be read or it isn't a validate LZIP header, or the dictionary
|
||||
@@ -248,43 +235,7 @@ public sealed partial class LZipStream : Stream, IStreamStack
|
||||
return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously reads the 6-byte header of the stream, and returns 0 if either the header
|
||||
/// couldn't be read or it isn't a validate LZIP header, or the dictionary
|
||||
/// size if it *is* a valid LZIP file.
|
||||
/// </summary>
|
||||
public static async ValueTask<int> ValidateAndReadSizeAsync(
|
||||
Stream stream,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
// Read the header
|
||||
byte[] header = new byte[6];
|
||||
var n = await stream
|
||||
.ReadAsync(header, 0, header.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// TODO: Handle reading only part of the header?
|
||||
|
||||
if (n != 6)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (
|
||||
header[0] != 'L'
|
||||
|| header[1] != 'Z'
|
||||
|| header[2] != 'I'
|
||||
|| header[3] != 'P'
|
||||
|| header[4] != 1 /* version 1 */
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var basePower = header[5] & 0x1F;
|
||||
var subtractionNumerator = (header[5] & 0xE0) >> 5;
|
||||
return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4)));
|
||||
}
|
||||
// Async methods moved to LZipStream.Async.cs
|
||||
|
||||
private static readonly byte[] headerBytes =
|
||||
[
|
||||
|
||||
@@ -13,6 +13,16 @@ internal sealed partial class MultiVolumeReadOnlyAsyncStream
|
||||
: MultiVolumeReadOnlyStreamBase,
|
||||
IStreamStack
|
||||
{
|
||||
internal static async ValueTask<MultiVolumeReadOnlyAsyncStream> Create(
|
||||
IAsyncEnumerable<RarFilePart> parts
|
||||
)
|
||||
{
|
||||
var stream = new MultiVolumeReadOnlyAsyncStream(parts);
|
||||
await stream.filePartEnumerator.MoveNextAsync();
|
||||
stream.InitializeNextFilePart();
|
||||
return stream;
|
||||
}
|
||||
|
||||
#if NET8_0_OR_GREATER
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
|
||||
@@ -44,15 +44,7 @@ internal sealed partial class MultiVolumeReadOnlyAsyncStream
|
||||
filePartEnumerator = parts.GetAsyncEnumerator();
|
||||
}
|
||||
|
||||
internal static async ValueTask<MultiVolumeReadOnlyAsyncStream> Create(
|
||||
IAsyncEnumerable<RarFilePart> parts
|
||||
)
|
||||
{
|
||||
var stream = new MultiVolumeReadOnlyAsyncStream(parts);
|
||||
await stream.filePartEnumerator.MoveNextAsync();
|
||||
stream.InitializeNextFilePart();
|
||||
return stream;
|
||||
}
|
||||
// Async methods moved to MultiVolumeReadOnlyAsyncStream.Async.cs
|
||||
|
||||
private void InitializeNextFilePart()
|
||||
{
|
||||
|
||||
@@ -10,6 +10,18 @@ namespace SharpCompress.Compressors.Rar;
|
||||
|
||||
internal partial class RarBLAKE2spStream : RarStream, IStreamStack
|
||||
{
|
||||
public static async ValueTask<RarBLAKE2spStream> CreateAsync(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyAsyncStream readStream,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var stream = new RarBLAKE2spStream(unpack, fileHeader, readStream);
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
|
||||
@@ -134,17 +134,7 @@ internal partial class RarBLAKE2spStream : RarStream, IStreamStack
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static async ValueTask<RarBLAKE2spStream> CreateAsync(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyAsyncStream readStream,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var stream = new RarBLAKE2spStream(unpack, fileHeader, readStream);
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
// Async methods moved to RarBLAKE2spStream.Async.cs
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,18 @@ namespace SharpCompress.Compressors.Rar;
|
||||
|
||||
internal partial class RarCrcStream : RarStream, IStreamStack
|
||||
{
|
||||
public static async ValueTask<RarCrcStream> CreateAsync(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStreamBase readStream,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var stream = new RarCrcStream(unpack, fileHeader, readStream);
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
|
||||
@@ -59,17 +59,7 @@ internal partial class RarCrcStream : RarStream, IStreamStack
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static async ValueTask<RarCrcStream> CreateAsync(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStreamBase readStream,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var stream = new RarCrcStream(unpack, fileHeader, readStream);
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
// Async methods moved to RarCrcStream.Async.cs
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
|
||||
107
src/SharpCompress/Readers/ReaderFactory.Async.cs
Normal file
107
src/SharpCompress/Readers/ReaderFactory.Async.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Factories;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Readers;
|
||||
|
||||
public static partial class ReaderFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Opens a Reader from a filepath asynchronously
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static ValueTask<IAsyncReader> OpenAsyncReader(
|
||||
string filePath,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
filePath.NotNullOrEmpty(nameof(filePath));
|
||||
return OpenAsyncReader(new FileInfo(filePath), options, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a Reader from a FileInfo asynchronously
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static ValueTask<IAsyncReader> OpenAsyncReader(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
return OpenAsyncReader(fileInfo.OpenRead(), options, cancellationToken);
|
||||
}
|
||||
|
||||
public static async ValueTask<IAsyncReader> OpenAsyncReader(
|
||||
Stream stream,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
stream.NotNull(nameof(stream));
|
||||
options ??= new ReaderOptions() { LeaveStreamOpen = false };
|
||||
|
||||
var bStream = new SharpCompressStream(stream, bufferSize: options.BufferSize);
|
||||
long pos = bStream.GetPosition();
|
||||
|
||||
var factories = Factory.Factories.OfType<Factory>();
|
||||
|
||||
Factory? testedFactory = null;
|
||||
if (!string.IsNullOrWhiteSpace(options.ExtensionHint))
|
||||
{
|
||||
testedFactory = factories.FirstOrDefault(a =>
|
||||
a.GetSupportedExtensions()
|
||||
.Contains(options.ExtensionHint, StringComparer.CurrentCultureIgnoreCase)
|
||||
);
|
||||
if (testedFactory is IReaderFactory readerFactory)
|
||||
{
|
||||
bStream.StackSeek(pos);
|
||||
if (
|
||||
await testedFactory.IsArchiveAsync(
|
||||
bStream,
|
||||
cancellationToken: cancellationToken
|
||||
)
|
||||
)
|
||||
{
|
||||
bStream.StackSeek(pos);
|
||||
return await readerFactory.OpenAsyncReader(bStream, options, cancellationToken);
|
||||
}
|
||||
}
|
||||
bStream.StackSeek(pos);
|
||||
}
|
||||
|
||||
foreach (var factory in factories)
|
||||
{
|
||||
if (testedFactory == factory)
|
||||
{
|
||||
continue; // Already tested above
|
||||
}
|
||||
bStream.StackSeek(pos);
|
||||
if (
|
||||
factory is IReaderFactory readerFactory
|
||||
&& await factory.IsArchiveAsync(bStream, cancellationToken: cancellationToken)
|
||||
)
|
||||
{
|
||||
bStream.StackSeek(pos);
|
||||
return await readerFactory.OpenAsyncReader(bStream, options, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidFormatException(
|
||||
"Cannot determine compressed stream type. Supported Reader Formats: Arc, Arj, Zip, GZip, BZip2, Tar, Rar, LZip, XZ, ZStandard"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Readers;
|
||||
|
||||
public static class ReaderFactory
|
||||
public static partial class ReaderFactory
|
||||
{
|
||||
public static IReader OpenReader(string filePath, ReaderOptions? options = null)
|
||||
{
|
||||
@@ -17,46 +17,12 @@ public static class ReaderFactory
|
||||
return OpenReader(new FileInfo(filePath), options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a Reader from a filepath asynchronously
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static ValueTask<IAsyncReader> OpenAsyncReader(
|
||||
string filePath,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
filePath.NotNullOrEmpty(nameof(filePath));
|
||||
return OpenAsyncReader(new FileInfo(filePath), options, cancellationToken);
|
||||
}
|
||||
|
||||
public static IReader OpenReader(FileInfo fileInfo, ReaderOptions? options = null)
|
||||
{
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
return OpenReader(fileInfo.OpenRead(), options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a Reader from a FileInfo asynchronously
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static ValueTask<IAsyncReader> OpenAsyncReader(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
return OpenAsyncReader(fileInfo.OpenRead(), options, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a Reader for Non-seeking usage
|
||||
/// </summary>
|
||||
@@ -110,63 +76,5 @@ public static class ReaderFactory
|
||||
);
|
||||
}
|
||||
|
||||
public static async ValueTask<IAsyncReader> OpenAsyncReader(
|
||||
Stream stream,
|
||||
ReaderOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
stream.NotNull(nameof(stream));
|
||||
options ??= new ReaderOptions() { LeaveStreamOpen = false };
|
||||
|
||||
var bStream = new SharpCompressStream(stream, bufferSize: options.BufferSize);
|
||||
long pos = bStream.GetPosition();
|
||||
|
||||
var factories = Factory.Factories.OfType<Factory>();
|
||||
|
||||
Factory? testedFactory = null;
|
||||
if (!string.IsNullOrWhiteSpace(options.ExtensionHint))
|
||||
{
|
||||
testedFactory = factories.FirstOrDefault(a =>
|
||||
a.GetSupportedExtensions()
|
||||
.Contains(options.ExtensionHint, StringComparer.CurrentCultureIgnoreCase)
|
||||
);
|
||||
if (testedFactory is IReaderFactory readerFactory)
|
||||
{
|
||||
bStream.StackSeek(pos);
|
||||
if (
|
||||
await testedFactory.IsArchiveAsync(
|
||||
bStream,
|
||||
cancellationToken: cancellationToken
|
||||
)
|
||||
)
|
||||
{
|
||||
bStream.StackSeek(pos);
|
||||
return await readerFactory.OpenAsyncReader(bStream, options, cancellationToken);
|
||||
}
|
||||
}
|
||||
bStream.StackSeek(pos);
|
||||
}
|
||||
|
||||
foreach (var factory in factories)
|
||||
{
|
||||
if (testedFactory == factory)
|
||||
{
|
||||
continue; // Already tested above
|
||||
}
|
||||
bStream.StackSeek(pos);
|
||||
if (
|
||||
factory is IReaderFactory readerFactory
|
||||
&& await factory.IsArchiveAsync(bStream, cancellationToken: cancellationToken)
|
||||
)
|
||||
{
|
||||
bStream.StackSeek(pos);
|
||||
return await readerFactory.OpenAsyncReader(bStream, options, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidFormatException(
|
||||
"Cannot determine compressed stream type. Supported Reader Formats: Arc, Arj, Zip, GZip, BZip2, Tar, Rar, LZip, XZ, ZStandard"
|
||||
);
|
||||
}
|
||||
// Async methods moved to ReaderFactory.Async.cs
|
||||
}
|
||||
|
||||
55
src/SharpCompress/Utility.Async.cs
Normal file
55
src/SharpCompress/Utility.Async.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress;
|
||||
|
||||
internal static partial class Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Read exactly the requested number of bytes from a stream asynchronously. Throws EndOfStreamException if not enough data is available.
|
||||
/// </summary>
|
||||
public static async ValueTask ReadExactAsync(
|
||||
this Stream stream,
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int length,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (stream is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
if (buffer is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (offset < 0 || offset > buffer.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
}
|
||||
|
||||
if (length < 0 || length > buffer.Length - offset)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
}
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
var fetched = await stream
|
||||
.ReadAsync(buffer, offset, length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (fetched <= 0)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
offset += fetched;
|
||||
length -= fetched;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress;
|
||||
|
||||
internal static class Utility
|
||||
internal static partial class Utility
|
||||
{
|
||||
//80kb is a good industry standard temporary buffer size
|
||||
internal const int TEMP_BUFFER_SIZE = 81920;
|
||||
@@ -336,51 +336,7 @@ internal static class Utility
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read exactly the requested number of bytes from a stream asynchronously. Throws EndOfStreamException if not enough data is available.
|
||||
/// </summary>
|
||||
public static async ValueTask ReadExactAsync(
|
||||
this Stream stream,
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int length,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (stream is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
if (buffer is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (offset < 0 || offset > buffer.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
}
|
||||
|
||||
if (length < 0 || length > buffer.Length - offset)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
}
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
var fetched = await stream
|
||||
.ReadAsync(buffer, offset, length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (fetched <= 0)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
offset += fetched;
|
||||
length -= fetched;
|
||||
}
|
||||
}
|
||||
// Async methods moved to Utility.Async.cs
|
||||
|
||||
public static string TrimNulls(this string source) => source.Replace('\0', ' ').Trim();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user