-
Notifications
You must be signed in to change notification settings - Fork 1
Add File extensions: WriteAllZeroes, ReadAllBytes(offset), ReadAllBytes(offset, length), ReadAllBytesAsync(offset), ReadAllBytesAsync(offset, length) #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Tyrrrz
merged 16 commits into
prime
from
copilot/add-file-extensions-with-async-version
Apr 12, 2026
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
98d914a
Add FileExtensions with WriteAllZeroes and ReadAllBytes (sync + async)
Copilot 5aa9d98
Remove spurious bufferSize:1 from WriteAllZeroes FileStream
Copilot ecf7af5
Merge branch 'prime' into copilot/add-file-extensions-with-async-version
Tyrrrz 896fb22
Update PowerKit/Extensions/FileExtensions.cs
Tyrrrz 1663629
Fix build errors: replace FileStreamOptions with FileOptions ctor, ca…
Copilot a9a4a41
Change offset to long, add ReadAllBytes/Async overloads with offset+l…
Copilot 36d7e9d
Use MemoryPool<byte>.Shared for buffer allocation in ReadAllBytes(Async)
Copilot f970532
Avoid redundant memory slicing by storing slice in local variable
Copilot 5f3abb3
Merge branch 'prime' into copilot/add-file-extensions-with-async-version
Tyrrrz c09c408
Guard offset/length, drop MemoryPool in ReadAllBytes(Async); add edge…
Copilot 43734d6
Return empty array instead of throwing when offset >= stream.Length
Copilot 55cc511
Use async ReadAllBytesAsync in WriteAllZeroes_Test
Copilot 63a574b
Guard length > int.MaxValue with ArgumentOutOfRangeException in offse…
Copilot 7312aff
Change length parameter from long to int in offset+length overloads
Copilot 3d8e839
Merge branch 'prime' into copilot/add-file-extensions-with-async-version
Tyrrrz 33caa32
Use TempFile.Create() in FileExtensionsTests instead of manual temp p…
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| using System.IO; | ||
| using System.Threading.Tasks; | ||
| using FluentAssertions; | ||
| using PowerKit; | ||
| using PowerKit.Extensions; | ||
| using Xunit; | ||
|
|
||
| namespace PowerKit.Tests; | ||
|
|
||
| public class FileExtensionsTests | ||
| { | ||
| [Fact] | ||
| public async Task WriteAllZeroes_Test() | ||
| { | ||
| // Arrange | ||
| using var tempFile = TempFile.Create(); | ||
|
|
||
| // Act | ||
| File.WriteAllZeroes(tempFile.Path, 1024); | ||
|
|
||
| // Assert | ||
| var bytes = await File.ReadAllBytesAsync(tempFile.Path); | ||
| bytes.Should().HaveCount(1024); | ||
| bytes.Should().AllSatisfy(b => b.Should().Be(0)); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void ReadAllBytes_WithOffset_Test() | ||
| { | ||
| // Arrange | ||
| using var tempFile = TempFile.Create(); | ||
| File.WriteAllBytes(tempFile.Path, [1, 2, 3, 4, 5]); | ||
|
|
||
| // Act | ||
| var bytes = File.ReadAllBytes(tempFile.Path, offset: 2L); | ||
|
|
||
| // Assert | ||
| bytes.Should().Equal(3, 4, 5); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void ReadAllBytes_WithOffset_AtEndOfFile_Test() | ||
| { | ||
| // Arrange | ||
| using var tempFile = TempFile.Create(); | ||
| File.WriteAllBytes(tempFile.Path, [1, 2, 3, 4, 5]); | ||
|
|
||
| // Act | ||
| var bytes = File.ReadAllBytes(tempFile.Path, offset: 5L); | ||
|
|
||
| // Assert | ||
| bytes.Should().BeEmpty(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void ReadAllBytes_WithOffset_PastEndOfFile_Test() | ||
| { | ||
| // Arrange | ||
| using var tempFile = TempFile.Create(); | ||
| File.WriteAllBytes(tempFile.Path, [1, 2, 3, 4, 5]); | ||
|
|
||
| // Act & Assert | ||
| var bytes = File.ReadAllBytes(tempFile.Path, offset: 10L); | ||
| bytes.Should().BeEmpty(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void ReadAllBytes_WithOffsetAndLength_Test() | ||
| { | ||
| // Arrange | ||
| using var tempFile = TempFile.Create(); | ||
| File.WriteAllBytes(tempFile.Path, [1, 2, 3, 4, 5]); | ||
|
|
||
| // Act | ||
| var bytes = File.ReadAllBytes(tempFile.Path, offset: 1L, length: 3); | ||
|
|
||
| // Assert | ||
| bytes.Should().Equal(2, 3, 4); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ReadAllBytesAsync_WithOffset_Test() | ||
| { | ||
| // Arrange | ||
| using var tempFile = TempFile.Create(); | ||
| File.WriteAllBytes(tempFile.Path, [1, 2, 3, 4, 5]); | ||
|
|
||
| // Act | ||
| var bytes = await File.ReadAllBytesAsync(tempFile.Path, offset: 2L); | ||
|
|
||
| // Assert | ||
| bytes.Should().Equal(3, 4, 5); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ReadAllBytesAsync_WithOffset_AtEndOfFile_Test() | ||
| { | ||
| // Arrange | ||
| using var tempFile = TempFile.Create(); | ||
| File.WriteAllBytes(tempFile.Path, [1, 2, 3, 4, 5]); | ||
|
|
||
| // Act | ||
| var bytes = await File.ReadAllBytesAsync(tempFile.Path, offset: 5L); | ||
|
|
||
| // Assert | ||
| bytes.Should().BeEmpty(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ReadAllBytesAsync_WithOffset_PastEndOfFile_Test() | ||
| { | ||
| // Arrange | ||
| using var tempFile = TempFile.Create(); | ||
| File.WriteAllBytes(tempFile.Path, [1, 2, 3, 4, 5]); | ||
|
|
||
| // Act & Assert | ||
| var bytes = await File.ReadAllBytesAsync(tempFile.Path, offset: 10L); | ||
| bytes.Should().BeEmpty(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ReadAllBytesAsync_WithOffsetAndLength_Test() | ||
| { | ||
| // Arrange | ||
| using var tempFile = TempFile.Create(); | ||
| File.WriteAllBytes(tempFile.Path, [1, 2, 3, 4, 5]); | ||
|
|
||
| // Act | ||
| var bytes = await File.ReadAllBytesAsync(tempFile.Path, offset: 1L, length: 3); | ||
|
|
||
| // Assert | ||
| bytes.Should().Equal(2, 3, 4); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| using System; | ||
| using System.IO; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace PowerKit.Extensions; | ||
|
|
||
| internal static class FileExtensions | ||
| { | ||
| extension(File) | ||
| { | ||
| /// <summary> | ||
| /// Creates a file at the specified path and fills it with zeroes. | ||
| /// </summary> | ||
| public static void WriteAllZeroes(string path, long count) | ||
| { | ||
| using var stream = new FileStream( | ||
| path, | ||
| FileMode.Create, | ||
| FileAccess.Write, | ||
| FileShare.None | ||
| ); | ||
|
|
||
| stream.SetLength(count); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Reads all bytes from the specified file starting at the given offset. | ||
| /// </summary> | ||
| public static byte[] ReadAllBytes(string path, long offset) | ||
| { | ||
| using var stream = new FileStream( | ||
| path, | ||
| FileMode.Open, | ||
| FileAccess.Read, | ||
| FileShare.ReadWrite | ||
| ); | ||
|
|
||
| stream.Seek(offset, SeekOrigin.Begin); | ||
|
|
||
| if (offset >= stream.Length) | ||
| { | ||
| return []; | ||
| } | ||
|
|
||
| var buffer = new byte[checked((int)(stream.Length - offset))]; | ||
| stream.ReadExactly(buffer); | ||
|
|
||
| return buffer; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Reads the specified number of bytes from the file starting at the given offset. | ||
| /// </summary> | ||
| public static byte[] ReadAllBytes(string path, long offset, int length) | ||
| { | ||
| using var stream = new FileStream( | ||
| path, | ||
| FileMode.Open, | ||
| FileAccess.Read, | ||
| FileShare.ReadWrite | ||
| ); | ||
|
|
||
| stream.Seek(offset, SeekOrigin.Begin); | ||
|
|
||
| if (length < 0) | ||
| { | ||
| throw new ArgumentOutOfRangeException(nameof(length)); | ||
| } | ||
|
|
||
| var buffer = new byte[length]; | ||
| stream.ReadExactly(buffer); | ||
|
|
||
|
Tyrrrz marked this conversation as resolved.
Tyrrrz marked this conversation as resolved.
|
||
| return buffer; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Reads all bytes from the specified file starting at the given offset asynchronously. | ||
| /// </summary> | ||
| public static async Task<byte[]> ReadAllBytesAsync( | ||
| string path, | ||
| long offset, | ||
| CancellationToken cancellationToken = default | ||
| ) | ||
| { | ||
| using var stream = new FileStream( | ||
| path, | ||
| FileMode.Open, | ||
| FileAccess.Read, | ||
| FileShare.ReadWrite, | ||
| bufferSize: 4096, | ||
| FileOptions.Asynchronous | ||
| ); | ||
|
|
||
| stream.Seek(offset, SeekOrigin.Begin); | ||
|
|
||
| if (offset >= stream.Length) | ||
| { | ||
| return []; | ||
| } | ||
|
|
||
| var buffer = new byte[checked((int)(stream.Length - offset))]; | ||
| await stream.ReadExactlyAsync(buffer, cancellationToken).ConfigureAwait(false); | ||
|
|
||
| return buffer; | ||
|
Tyrrrz marked this conversation as resolved.
|
||
| } | ||
|
|
||
| /// <summary> | ||
| /// Reads the specified number of bytes from the file starting at the given offset asynchronously. | ||
| /// </summary> | ||
| public static async Task<byte[]> ReadAllBytesAsync( | ||
| string path, | ||
| long offset, | ||
| int length, | ||
| CancellationToken cancellationToken = default | ||
| ) | ||
| { | ||
| using var stream = new FileStream( | ||
| path, | ||
| FileMode.Open, | ||
| FileAccess.Read, | ||
| FileShare.ReadWrite, | ||
| bufferSize: 4096, | ||
| FileOptions.Asynchronous | ||
| ); | ||
|
|
||
| stream.Seek(offset, SeekOrigin.Begin); | ||
|
|
||
| if (length < 0) | ||
| { | ||
| throw new ArgumentOutOfRangeException(nameof(length)); | ||
| } | ||
|
|
||
| var buffer = new byte[length]; | ||
| await stream.ReadExactlyAsync(buffer, cancellationToken).ConfigureAwait(false); | ||
|
|
||
|
Tyrrrz marked this conversation as resolved.
Tyrrrz marked this conversation as resolved.
|
||
| return buffer; | ||
| } | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.