fixed up async writer

This commit is contained in:
Adam Hathcock
2026-01-13 13:54:15 +00:00
parent 9a4e864f5e
commit 9cf2b3129c
13 changed files with 138 additions and 127 deletions

View File

@@ -182,7 +182,7 @@ public class GZipFactory
}
/// <inheritdoc/>
public IWriter OpenAsync(
public IAsyncWriter OpenAsync(
Stream stream,
WriterOptions writerOptions,
CancellationToken cancellationToken = default
@@ -193,7 +193,7 @@ public class GZipFactory
{
throw new InvalidFormatException("GZip archives only support GZip compression type.");
}
return Open(stream, writerOptions);
return (IAsyncWriter)Open(stream, writerOptions);
}
#endregion

View File

@@ -294,14 +294,14 @@ public class TarFactory
new TarWriter(stream, new TarWriterOptions(writerOptions));
/// <inheritdoc/>
public IWriter OpenAsync(
public IAsyncWriter OpenAsync(
Stream stream,
WriterOptions writerOptions,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return Open(stream, writerOptions);
return (IAsyncWriter)Open(stream, writerOptions);
}
#endregion

View File

@@ -218,14 +218,14 @@ public class ZipFactory
new ZipWriter(stream, new ZipWriterOptions(writerOptions));
/// <inheritdoc/>
public IWriter OpenAsync(
public IAsyncWriter OpenAsync(
Stream stream,
WriterOptions writerOptions,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return Open(stream, writerOptions);
return (IAsyncWriter)Open(stream, writerOptions);
}
#endregion

View File

@@ -8,7 +8,7 @@ using SharpCompress.IO;
namespace SharpCompress.Writers;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public abstract class AbstractWriter(ArchiveType type, WriterOptions writerOptions) : IWriter
public abstract class AbstractWriter(ArchiveType type, WriterOptions writerOptions) : IWriter, IAsyncWriter
{
private bool _isDisposed;

View File

@@ -10,13 +10,18 @@ public interface IWriter : IDisposable
{
ArchiveType WriterType { get; }
void Write(string filename, Stream source, DateTime? modificationTime);
void WriteDirectory(string directoryName, DateTime? modificationTime);
}
public interface IAsyncWriter : IDisposable
{
ArchiveType WriterType { get; }
ValueTask WriteAsync(
string filename,
Stream source,
DateTime? modificationTime,
CancellationToken cancellationToken = default
);
void WriteDirectory(string directoryName, DateTime? modificationTime);
ValueTask WriteDirectoryAsync(
string directoryName,
DateTime? modificationTime,

View File

@@ -8,126 +8,118 @@ namespace SharpCompress.Writers;
public static class IWriterExtensions
{
public static void Write(this IWriter writer, string entryPath, Stream source) =>
writer.Write(entryPath, source, null);
public static void Write(this IWriter writer, string entryPath, FileInfo source)
extension(IWriter writer)
{
if (!source.Exists)
public void Write(string entryPath, Stream source) =>
writer.Write(entryPath, source, null);
public void Write(string entryPath, FileInfo source)
{
throw new ArgumentException("Source does not exist: " + source.FullName);
}
using var stream = source.OpenRead();
writer.Write(entryPath, stream, source.LastWriteTime);
}
if (!source.Exists)
{
throw new ArgumentException("Source does not exist: " + source.FullName);
}
public static void Write(this IWriter writer, string entryPath, string source) =>
writer.Write(entryPath, new FileInfo(source));
public static void WriteAll(
this IWriter writer,
string directory,
string searchPattern = "*",
SearchOption option = SearchOption.TopDirectoryOnly
) => writer.WriteAll(directory, searchPattern, null, option);
public static void WriteAll(
this IWriter writer,
string directory,
string searchPattern = "*",
Func<string, bool>? fileSearchFunc = null,
SearchOption option = SearchOption.TopDirectoryOnly
)
{
if (!Directory.Exists(directory))
{
throw new ArgumentException("Directory does not exist: " + directory);
using var stream = source.OpenRead();
writer.Write(entryPath, stream, source.LastWriteTime);
}
fileSearchFunc ??= n => true;
foreach (
var file in Directory
.EnumerateFiles(directory, searchPattern, option)
.Where(fileSearchFunc)
public void Write(string entryPath, string source) =>
writer.Write(entryPath, new FileInfo(source));
public void WriteAll(string directory,
string searchPattern = "*",
SearchOption option = SearchOption.TopDirectoryOnly
) => writer.WriteAll(directory, searchPattern, null, option);
public void WriteAll(string directory,
string searchPattern = "*",
Func<string, bool>? fileSearchFunc = null,
SearchOption option = SearchOption.TopDirectoryOnly
)
{
writer.Write(file.Substring(directory.Length), file);
if (!Directory.Exists(directory))
{
throw new ArgumentException("Directory does not exist: " + directory);
}
fileSearchFunc ??= n => true;
foreach (
var file in Directory
.EnumerateFiles(directory, searchPattern, option)
.Where(fileSearchFunc)
)
{
writer.Write(file.Substring(directory.Length), file);
}
}
public void WriteDirectory(string directoryName) =>
writer.WriteDirectory(directoryName, null);
}
public static void WriteDirectory(this IWriter writer, string directoryName) =>
writer.WriteDirectory(directoryName, null);
extension(IAsyncWriter writer)
{
public ValueTask WriteAsync(string entryPath,
Stream source,
CancellationToken cancellationToken = default
) => writer.WriteAsync(entryPath, source, null, cancellationToken);
public async ValueTask WriteAsync(string entryPath,
FileInfo source,
CancellationToken cancellationToken = default
)
{
if (!source.Exists)
{
throw new ArgumentException("Source does not exist: " + source.FullName);
}
using var stream = source.OpenRead();
await writer
.WriteAsync(entryPath, stream, source.LastWriteTime, cancellationToken)
.ConfigureAwait(false);
}
public ValueTask WriteAsync(string entryPath,
string source,
CancellationToken cancellationToken = default
) => writer.WriteAsync(entryPath, new FileInfo(source), cancellationToken);
public ValueTask WriteAllAsync(string directory,
string searchPattern = "*",
SearchOption option = SearchOption.TopDirectoryOnly,
CancellationToken cancellationToken = default
) => writer.WriteAllAsync(directory, searchPattern, null, option, cancellationToken);
public async ValueTask WriteAllAsync(string directory,
string searchPattern = "*",
Func<string, bool>? fileSearchFunc = null,
SearchOption option = SearchOption.TopDirectoryOnly,
CancellationToken cancellationToken = default
)
{
if (!Directory.Exists(directory))
{
throw new ArgumentException("Directory does not exist: " + directory);
}
fileSearchFunc ??= n => true;
foreach (
var file in Directory
.EnumerateFiles(directory, searchPattern, option)
.Where(fileSearchFunc)
)
{
await writer
.WriteAsync(file.Substring(directory.Length), file, cancellationToken)
.ConfigureAwait(false);
}
}
public ValueTask WriteDirectoryAsync(string directoryName,
CancellationToken cancellationToken = default
) => writer.WriteDirectoryAsync(directoryName, null, cancellationToken);
}
// Async extensions
public static ValueTask WriteAsync(
this IWriter writer,
string entryPath,
Stream source,
CancellationToken cancellationToken = default
) => writer.WriteAsync(entryPath, source, null, cancellationToken);
public static async ValueTask WriteAsync(
this IWriter writer,
string entryPath,
FileInfo source,
CancellationToken cancellationToken = default
)
{
if (!source.Exists)
{
throw new ArgumentException("Source does not exist: " + source.FullName);
}
using var stream = source.OpenRead();
await writer
.WriteAsync(entryPath, stream, source.LastWriteTime, cancellationToken)
.ConfigureAwait(false);
}
public static ValueTask WriteAsync(
this IWriter writer,
string entryPath,
string source,
CancellationToken cancellationToken = default
) => writer.WriteAsync(entryPath, new FileInfo(source), cancellationToken);
public static ValueTask WriteAllAsync(
this IWriter writer,
string directory,
string searchPattern = "*",
SearchOption option = SearchOption.TopDirectoryOnly,
CancellationToken cancellationToken = default
) => writer.WriteAllAsync(directory, searchPattern, null, option, cancellationToken);
public static async ValueTask WriteAllAsync(
this IWriter writer,
string directory,
string searchPattern = "*",
Func<string, bool>? fileSearchFunc = null,
SearchOption option = SearchOption.TopDirectoryOnly,
CancellationToken cancellationToken = default
)
{
if (!Directory.Exists(directory))
{
throw new ArgumentException("Directory does not exist: " + directory);
}
fileSearchFunc ??= n => true;
foreach (
var file in Directory
.EnumerateFiles(directory, searchPattern, option)
.Where(fileSearchFunc)
)
{
await writer
.WriteAsync(file.Substring(directory.Length), file, cancellationToken)
.ConfigureAwait(false);
}
}
public static ValueTask WriteDirectoryAsync(
this IWriter writer,
string directoryName,
CancellationToken cancellationToken = default
) => writer.WriteDirectoryAsync(directoryName, null, cancellationToken);
}

View File

@@ -8,7 +8,7 @@ public interface IWriterFactory : IFactory
{
IWriter Open(Stream stream, WriterOptions writerOptions);
IWriter OpenAsync(
IAsyncWriter OpenAsync(
Stream stream,
WriterOptions writerOptions,
CancellationToken cancellationToken = default

View File

@@ -31,7 +31,7 @@ public static class WriterFactory
/// <param name="writerOptions">Writer options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task that returns an IWriter.</returns>
public static IWriter OpenAsync(
public static IAsyncWriter OpenAsync(
Stream stream,
ArchiveType archiveType,
WriterOptions writerOptions,

View File

@@ -380,6 +380,20 @@ public class ArchiveTests : ReaderTests
return WriterFactory.Open(stream, ArchiveType.Zip, writerOptions);
}
protected static IAsyncWriter CreateWriterWithLevelAsync(
Stream stream,
CompressionType compressionType,
int? compressionLevel = null
)
{
var writerOptions = new ZipWriterOptions(compressionType);
if (compressionLevel.HasValue)
{
writerOptions.CompressionLevel = compressionLevel.Value;
}
return WriterFactory.OpenAsync(stream, ArchiveType.Zip, writerOptions);
}
/// <summary>
/// Verifies archive content against expected files with CRC32 validation
/// </summary>

View File

@@ -22,7 +22,7 @@ public class GZipWriterAsyncTests : WriterTests
FileAccess.Write
)
)
using (var writer = WriterFactory.Open(stream, ArchiveType.GZip, CompressionType.GZip))
using (var writer = WriterFactory.OpenAsync(stream, ArchiveType.GZip, CompressionType.GZip))
{
await writer.WriteAsync("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"));
}

View File

@@ -32,7 +32,7 @@ public class TarArchiveAsyncTests : ArchiveTests
// Step 1: create a tar file containing a file with the test name
using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH2_FILES_PATH, archive)))
using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, CompressionType.None))
using (var writer = WriterFactory.OpenAsync(stream, ArchiveType.Tar, CompressionType.None))
using (Stream inputStream = new MemoryStream())
{
var sw = new StreamWriter(inputStream);
@@ -76,7 +76,7 @@ public class TarArchiveAsyncTests : ArchiveTests
// Step 1: create a tar file containing a file with a long name
using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH2_FILES_PATH, archive)))
using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, CompressionType.None))
using (var writer = WriterFactory.OpenAsync(stream, ArchiveType.Tar, CompressionType.None))
using (Stream inputStream = new MemoryStream())
{
var sw = new StreamWriter(inputStream);

View File

@@ -73,7 +73,7 @@ public class WriterTests : TestBase
writerOptions.ArchiveEncoding.Default = encoding ?? Encoding.Default;
using var writer = WriterFactory.Open(stream, _type, writerOptions);
using var writer = WriterFactory.OpenAsync(stream, _type, writerOptions);
await writer.WriteAllAsync(
ORIGINAL_FILES_PATH,
"*",

View File

@@ -60,7 +60,7 @@ public class ZipTypesLevelsWithCrcRatioAsyncTests : ArchiveTests
// Create zip archive in memory
using var zipStream = new MemoryStream();
using (var writer = CreateWriterWithLevel(zipStream, compressionType, compressionLevel))
using (var writer = CreateWriterWithLevelAsync(zipStream, compressionType, compressionLevel))
{
await writer.WriteAsync($"file1_{sizeMb}MiB.txt", new MemoryStream(file1Data));
await writer.WriteAsync($"data/file2_{sizeMb * 2}MiB.txt", new MemoryStream(file2Data));
@@ -129,7 +129,7 @@ public class ZipTypesLevelsWithCrcRatioAsyncTests : ArchiveTests
CompressionLevel = compressionLevel,
};
using (var writer = WriterFactory.Open(zipStream, ArchiveType.Zip, writerOptions))
using (var writer = WriterFactory.OpenAsync(zipStream, ArchiveType.Zip, writerOptions))
{
await writer.WriteAsync(
$"{compressionType}_level_{compressionLevel}_{sizeMb}MiB.txt",
@@ -191,7 +191,7 @@ public class ZipTypesLevelsWithCrcRatioAsyncTests : ArchiveTests
// Create archive with specified compression and level
using var zipStream = new MemoryStream();
using (var writer = CreateWriterWithLevel(zipStream, compressionType, compressionLevel))
using (var writer = CreateWriterWithLevelAsync(zipStream, compressionType, compressionLevel))
{
await writer.WriteAsync(
$"{compressionType}_{compressionLevel}_{sizeMb}MiB.txt",