Skip to content

Commit 668d599

Browse files
authored
Merge pull request #1354 from adamhathcock/adam/instance-buffer-size
Instanced buffer size
2 parents 5de1193 + 323831c commit 668d599

27 files changed

Lines changed: 477 additions & 41 deletions

.config/dotnet-tools.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
"isRoot": true,
44
"tools": {
55
"csharpier": {
6-
"version": "1.2.6",
6+
"version": "1.3.0",
77
"commands": [
88
"csharpier"
99
],
1010
"rollForward": false
1111
}
1212
}
13-
}
13+
}

docs/API.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ using (var archive = GZipArchive.CreateArchive())
9292
// With fluent options (preferred)
9393
var options = WriterOptions.ForZip()
9494
.WithCompressionLevel(9)
95-
.WithLeaveStreamOpen(false);
95+
.WithLeaveStreamOpen(false)
96+
.WithBufferSize(131072);
9697
using (var archive = ZipArchive.CreateArchive())
9798
{
9899
archive.SaveTo("output.zip", options);
@@ -102,10 +103,13 @@ using (var archive = ZipArchive.CreateArchive())
102103
var options2 = new WriterOptions(CompressionType.Deflate)
103104
{
104105
CompressionLevel = 9,
105-
LeaveStreamOpen = false
106+
LeaveStreamOpen = false,
107+
BufferSize = 131072
106108
};
107109
```
108110

111+
`WriterOptions.BufferSize` controls stream copy buffers used while writing archive entries. If it is not set, SharpCompress falls back to `Constants.BufferSize`.
112+
109113
---
110114

111115
## Archive API Methods
@@ -315,6 +319,9 @@ var safeOptions = ExtractionOptions.SafeExtract; // No overwrite
315319
var flatOptions = ExtractionOptions.FlatExtract; // No directory structure
316320
var metadataOptions = ExtractionOptions.PreserveMetadata; // Keep timestamps and attributes
317321
322+
// Tune extraction copy buffering
323+
var extractionOptions = new ExtractionOptions { BufferSize = 131072 };
324+
318325
// Factory defaults:
319326
// - file path / FileInfo overloads use LeaveStreamOpen = false
320327
// - stream overloads use LeaveStreamOpen = true
@@ -334,6 +341,13 @@ var options = new ReaderOptions
334341
BufferSize = 81920,
335342
RewindableBufferSize = 1_048_576,
336343
};
344+
345+
var extractionOptions = new ExtractionOptions
346+
{
347+
ExtractFullPath = true,
348+
Overwrite = true,
349+
BufferSize = 131072,
350+
};
337351
```
338352

339353
### WriterOptions

docs/USAGE.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@ using (var archive = RarArchive.OpenArchive("Test.rar", ReaderOptions.ForFilePat
100100
// Simple extraction with RarArchive; this WriteToDirectory pattern works for all archive types
101101
archive.WriteToDirectory(
102102
@"D:\temp",
103-
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
103+
new ExtractionOptions
104+
{
105+
ExtractFullPath = true,
106+
Overwrite = true,
107+
BufferSize = 131072,
108+
}
104109
);
105110
}
106111
```

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": {
33
"version": "10.0.300",
4-
"rollForward": "latestPatch"
4+
"rollForward": "disable"
55
}
66
}

src/SharpCompress/Archives/IArchiveEntryExtensions.cs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ public static class IArchiveEntryExtensions
1717
/// </summary>
1818
/// <param name="streamToWriteTo">The stream to write the entry content to.</param>
1919
/// <param name="progress">Optional progress reporter for tracking extraction progress.</param>
20-
public void WriteTo(Stream streamToWriteTo, IProgress<ProgressReport>? progress = null)
20+
public void WriteTo(Stream streamToWriteTo, IProgress<ProgressReport>? progress = null) =>
21+
archiveEntry.WriteTo(streamToWriteTo, null, progress);
22+
23+
private void WriteTo(
24+
Stream streamToWriteTo,
25+
int? bufferSize,
26+
IProgress<ProgressReport>? progress = null
27+
)
2128
{
2229
if (archiveEntry.IsDirectory)
2330
{
@@ -26,7 +33,7 @@ public void WriteTo(Stream streamToWriteTo, IProgress<ProgressReport>? progress
2633

2734
using var entryStream = archiveEntry.OpenEntryStream();
2835
var sourceStream = WrapWithProgress(entryStream, archiveEntry, progress);
29-
sourceStream.CopyTo(streamToWriteTo, Constants.BufferSize);
36+
sourceStream.CopyTo(streamToWriteTo, bufferSize ?? Constants.BufferSize);
3037
}
3138

3239
/// <summary>
@@ -40,6 +47,18 @@ public async ValueTask WriteToAsync(
4047
IProgress<ProgressReport>? progress = null,
4148
CancellationToken cancellationToken = default
4249
)
50+
{
51+
await archiveEntry
52+
.WriteToAsync(streamToWriteTo, Constants.BufferSize, progress, cancellationToken)
53+
.ConfigureAwait(false);
54+
}
55+
56+
private async ValueTask WriteToAsync(
57+
Stream streamToWriteTo,
58+
int? bufferSize,
59+
IProgress<ProgressReport>? progress = null,
60+
CancellationToken cancellationToken = default
61+
)
4362
{
4463
if (archiveEntry.IsDirectory)
4564
{
@@ -57,7 +76,7 @@ public async ValueTask WriteToAsync(
5776
#endif
5877
var sourceStream = WrapWithProgress(entryStream, archiveEntry, progress);
5978
await sourceStream
60-
.CopyToAsync(streamToWriteTo, Constants.BufferSize, cancellationToken)
79+
.CopyToAsync(streamToWriteTo, bufferSize ?? Constants.BufferSize, cancellationToken)
6180
.ConfigureAwait(false);
6281
}
6382
}
@@ -133,16 +152,18 @@ await entry.WriteToFileAsync(path, options, ct).ConfigureAwait(false),
133152
/// <summary>
134153
/// Extract to specific file
135154
/// </summary>
136-
public void WriteToFile(string destinationFileName, ExtractionOptions? options = null) =>
155+
public void WriteToFile(string destinationFileName, ExtractionOptions? options = null)
156+
{
137157
entry.WriteEntryToFile(
138158
destinationFileName,
139159
options,
140160
(x, fm) =>
141161
{
142-
using var fs = File.Open(destinationFileName, fm);
143-
entry.WriteTo(fs);
162+
using var fs = File.Open(x, fm);
163+
entry.WriteTo(fs, options?.BufferSize ?? Constants.BufferSize);
144164
}
145165
);
166+
}
146167

147168
/// <summary>
148169
/// Extract to specific file asynchronously
@@ -158,8 +179,10 @@ await entry
158179
options,
159180
async (x, fm, ct) =>
160181
{
161-
using var fs = File.Open(destinationFileName, fm);
162-
await entry.WriteToAsync(fs, null, ct).ConfigureAwait(false);
182+
using var fs = File.Open(x, fm);
183+
await entry
184+
.WriteToAsync(fs, options?.BufferSize, null, ct)
185+
.ConfigureAwait(false);
163186
},
164187
cancellationToken
165188
)

src/SharpCompress/Common/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public static class Constants
88
/// The default buffer size for stream operations, matching .NET's Stream.CopyTo default of 81920 bytes.
99
/// This can be modified globally at runtime.
1010
/// </summary>
11+
// TODO: Revisit remaining non-extraction usages after extraction buffering moves to ExtractionOptions.
1112
public static int BufferSize { get; set; } = 81920;
1213

1314
/// <summary>

src/SharpCompress/Common/ExtractionOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ public sealed record ExtractionOptions : IExtractionOptions
3838
/// </summary>
3939
public bool PreserveAttributes { get; set; }
4040

41+
/// <summary>
42+
/// Buffer size for extraction stream copy operations.
43+
/// </summary>
44+
public int BufferSize { get; set; } = Constants.BufferSize;
45+
4146
/// <summary>
4247
/// Delegate for writing symbolic links to disk.
4348
/// The first parameter is the source path (where the symlink is created).

src/SharpCompress/Common/Options/IExtractionOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public interface IExtractionOptions
3030
/// </summary>
3131
bool PreserveAttributes { get; set; }
3232

33+
/// <summary>
34+
/// Buffer size for extraction stream copy operations.
35+
/// </summary>
36+
int BufferSize { get; set; }
37+
3338
/// <summary>
3439
/// Delegate for writing symbolic links to disk.
3540
/// The first parameter is the source path (where the symlink is created).

src/SharpCompress/Common/Options/IWriterOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ public interface IWriterOptions : IStreamOptions, IEncodingOptions, IProgressOpt
1919
/// </summary>
2020
int CompressionLevel { get; set; }
2121

22+
/// <summary>
23+
/// Buffer size for writer stream copy operations.
24+
/// </summary>
25+
int BufferSize { get; set; }
26+
2227
/// <summary>
2328
/// Registry of compression providers.
2429
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom providers, such as

src/SharpCompress/Readers/AbstractReader.Async.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Threading;
66
using System.Threading.Tasks;
77
using SharpCompress.Common;
8-
using SharpCompress.IO;
98

109
namespace SharpCompress.Readers;
1110

@@ -138,19 +137,19 @@ public async ValueTask WriteEntryToAsync(
138137
_wroteCurrentEntry = true;
139138
}
140139

141-
internal async ValueTask WriteAsync(Stream writeStream, CancellationToken cancellationToken)
140+
private async ValueTask WriteAsync(Stream writeStream, CancellationToken cancellationToken)
142141
{
143142
#if LEGACY_DOTNET
144143
using Stream s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false);
145144
var sourceStream = WrapWithProgress(s, Entry);
146145
await sourceStream
147-
.CopyToAsync(writeStream, Constants.BufferSize, cancellationToken)
146+
.CopyToAsync(writeStream, Options.BufferSize, cancellationToken)
148147
.ConfigureAwait(false);
149148
#else
150149
await using Stream s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false);
151150
var sourceStream = WrapWithProgress(s, Entry);
152151
await sourceStream
153-
.CopyToAsync(writeStream, Constants.BufferSize, cancellationToken)
152+
.CopyToAsync(writeStream, Options.BufferSize, cancellationToken)
154153
.ConfigureAwait(false);
155154
#endif
156155
}
@@ -187,9 +186,7 @@ internal virtual ValueTask<bool> NextEntryForCurrentStreamAsync() =>
187186
/// <summary>
188187
/// Moves the current async enumerator to the next entry.
189188
/// </summary>
190-
internal virtual ValueTask<bool> NextEntryForCurrentStreamAsync(
191-
CancellationToken cancellationToken
192-
)
189+
private ValueTask<bool> NextEntryForCurrentStreamAsync(CancellationToken cancellationToken)
193190
{
194191
if (_entriesForCurrentReadStreamAsync is not null)
195192
{
@@ -202,6 +199,9 @@ CancellationToken cancellationToken
202199
// Async iterator method
203200
protected virtual async IAsyncEnumerable<TEntry> GetEntriesAsync(Stream stream)
204201
{
202+
#pragma warning disable VSTHRD111
203+
await Task.CompletedTask;
204+
#pragma warning restore VSTHRD111
205205
foreach (var entry in GetEntries(stream))
206206
{
207207
yield return entry;

0 commit comments

Comments
 (0)