Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ jobs:
10.x
- name: Checkout
uses: actions/checkout@v4
- name: Build
- name: Build (Debug)
run: dotnet build -c Debug
- name: Test
run: dotnet test --no-build --filter "TestCategory!~Cloud"
- name: Build (Release)
run: dotnet build -c Release
- name: Test (Debug)
run: dotnet test -c Debug --no-build --filter "TestCategory!~Cloud"
- name: Test (Release)
run: dotnet test -c Release --no-build --filter "TestCategory!~Cloud"

azure:
name: "Test: AzureFileSystem"
Expand Down Expand Up @@ -88,4 +92,4 @@ jobs:
- name: Build
run: dotnet build -c Debug
- name: Test
run: dotnet test --no-build --filter TestCategory=Cloud:Amazon
run: dotnet test --no-build --filter TestCategory=Cloud:Amazon -maxcpucount:1
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<PackageVersion Include="Azure.Storage.Blobs" Version="12.27.0" />
<PackageVersion Include="Azure.Storage.Blobs.Batch" Version="12.24.0" />
<PackageVersion Include="Google.Cloud.Storage.V1" Version="4.14.0" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Abstractions" Version="6.0.1" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="6.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Description>A .NET library that provides a virtual file system abstraction.</Description>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#if NET6_0
using System.Buffers;
using System.Text;

// ReSharper disable once CheckNamespace
namespace System.IO;
/// <summary>
/// Provides extension methods for the <see cref="StreamReader"/> to offer API compatibility with newer .NET versions.
/// </summary>
public static class StreamReaderExtensions
{
/// <summary>
/// Reads a line of characters asynchronously from the current stream and returns the data as a string.
/// </summary>
/// <param name="reader">The <see cref="StreamReader"/> to read from.</param>
/// <param name="cancellationToken">An optional cancellation token to cancel the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that represents the asynchronous read operation.
/// The value of the task contains the next line from the stream, or is <see langword="null"/>
/// if the end of the stream is reached.
/// </returns>
public static ValueTask<string?> ReadLineAsync(this StreamReader reader, CancellationToken cancellationToken = default)
{
if (cancellationToken.IsCancellationRequested)
return ValueTask.FromCanceled<string?>(cancellationToken);

return new ValueTask<string?>(reader.ReadLineAsync());
}

/// <summary>
/// Reads all characters from the current position to the end of the stream asynchronously and returns them as one string.
/// </summary>
/// <param name="reader">The <see cref="StreamReader"/> to read from.</param>
/// <param name="cancellationToken">An optional cancellation token to cancel the operation.</param>
/// <returns>
/// A <see cref="Task{TResult}"/> that represents the asynchronous read operation.
/// The value of the task contains a string with the characters from the current position to the end of the stream.
/// </returns>
public static async Task<string> ReadToEndAsync(this StreamReader reader, CancellationToken cancellationToken = default)
{
const int BufferSize = 4096;

var chars = ArrayPool<char>.Shared.Rent(BufferSize);
var sb = new StringBuilder();

while (true)
{
var count = await reader
.ReadAsync(new Memory<char>(chars), cancellationToken)
.ConfigureAwait(false);

if (count == 0)
break;

sb.Append(chars.AsSpan(0, count));
}

ArrayPool<char>.Shared.Return(chars);
return sb.ToString();
}
}
#endif
72 changes: 9 additions & 63 deletions src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,44 +74,11 @@ public static ValueTask<string> ReadAllTextAsync(this VirtualFile file, Cancella
/// </returns>
public static async ValueTask<string> ReadAllTextAsync(this VirtualFile file, Encoding? encoding, CancellationToken cancellationToken = default)
{
//
// Use a manual read loop since .NET 6 lacks a StreamReader.ReadToEndAsync overload that accepts a CancellationToken.
// This ensures the operation remains responsive to cancellation requests.
//

const int BufferSize = 4096;

// ReSharper disable once UseAwaitUsing
using var stream = await file.OpenReadAsync(cancellationToken).ConfigureAwait(false);
var reader = new StreamReader(stream, encoding ??= Encoding.UTF8);
var buffer = (char[]?)null;

try
{
cancellationToken.ThrowIfCancellationRequested();

buffer = ArrayPool<char>.Shared.Rent(encoding.GetMaxCharCount(BufferSize));
var sb = new StringBuilder();

while (true)
{
var count = await reader
.ReadAsync(new Memory<char>(buffer), cancellationToken)
.ConfigureAwait(false);

if (count == 0)
return sb.ToString();

sb.Append(buffer.AsSpan(0, count));
}
}
finally
{
reader.Dispose();
using var reader = new StreamReader(stream, encoding!);

if (buffer is not null)
ArrayPool<char>.Shared.Return(buffer);
}
return await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -142,11 +109,8 @@ public static async ValueTask<string[]> ReadAllLinesAsync(this VirtualFile file,
using var reader = new StreamReader(stream, encoding!);

var list = new List<string>();
while (await reader.ReadLineAsync().ConfigureAwait(false) is { } line)
{
cancellationToken.ThrowIfCancellationRequested();
while (await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false) is { } line)
list.Add(line);
}

return list.ToArray();
}
Expand All @@ -165,7 +129,7 @@ public static async ValueTask<byte[]> ReadAllBytesAsync(this VirtualFile file, C
// ReSharper disable once UseAwaitUsing
using var stream = await file.OpenReadAsync(cancellationToken).ConfigureAwait(false);

var length = GetStreamLength(stream);
var length = stream.CanSeek ? stream.Length : 0;
if (length > Array.MaxLength)
throw new IOException("The file is too large.");

Expand All @@ -179,15 +143,15 @@ static async ValueTask<byte[]> ReadAllBytesImplAsync(Stream stream, Cancellation
{
var bytes = new byte[stream.Length];
var index = 0;

do
{
var count = await stream.ReadAsync(bytes.AsMemory(index), cancellationToken).ConfigureAwait(false);
if (count == 0)
Error_EndOfStream();

index += count;
}
while (index < bytes.Length);
} while (index < bytes.Length);

return bytes;
}
Expand Down Expand Up @@ -225,29 +189,11 @@ static byte[] ResizeBuffer(byte[] oldArray)
var newArray = ArrayPool<byte>.Shared.Rent((int)length);
oldArray.AsSpan().TryCopyTo(newArray);

var rented = oldArray;
oldArray = newArray;

ArrayPool<byte>.Shared.Return(rented);
return oldArray;
ArrayPool<byte>.Shared.Return(oldArray);
return newArray;
}
}

static long GetStreamLength(Stream stream)
{
try
{
if (stream.CanSeek)
return stream.Length;
}
catch
{
// skip
}

return 0;
}

static void Error_EndOfStream() =>
throw new EndOfStreamException();
}
Expand Down Expand Up @@ -334,7 +280,7 @@ public static async ValueTask WriteAllLinesAsync(this VirtualFile file, IEnumera
await using var writer = new StreamWriter(stream, encoding, bufferSize: -1, leaveOpen: false);

foreach (var line in contents)
await writer.WriteLineAsync(line).ConfigureAwait(false);
await writer.WriteLineAsync(line.AsMemory(), cancellationToken).ConfigureAwait(false);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Description>Provides adapters for integrating Ramstack.FileSystem with Microsoft.Extensions.FileProviders.</Description>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down Expand Up @@ -43,8 +43,15 @@
</Compile>
</ItemGroup>

<ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" VersionOverride="6.0.1" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Description>Provides an implementation of Ramstack.FileSystem using Amazon S3 storage.</Description>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Description>Provides an implementation of Ramstack.FileSystem that combines multiple file systems into a single composite file system.</Description>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
6 changes: 3 additions & 3 deletions src/Ramstack.FileSystem.Globbing/Internal/PathHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ public PathSegmentIterator() =>
if (Avx2.IsSupported && (int)_position + Vector256<ushort>.Count <= length)
{
var chunk = LoadVector256(ref source, _position);
var slash = Vector256.Create('/');
var slash = Vector256.Create((ushort)'/');
var comparison = Avx2.CompareEqual(chunk, slash);

//
Expand All @@ -269,7 +269,7 @@ public PathSegmentIterator() =>
else if (Sse2.IsSupported && !Avx2.IsSupported && (int)_position + Vector128<ushort>.Count <= length)
{
var chunk = LoadVector128(ref source, _position);
var slash = Vector128.Create('/');
var slash = Vector128.Create((ushort)'/');
var comparison = Sse2.CompareEqual(chunk, slash);

//
Expand All @@ -289,7 +289,7 @@ public PathSegmentIterator() =>
else if (AdvSimd.Arm64.IsSupported && (int)_position + Vector128<ushort>.Count <= length)
{
var chunk = LoadVector128(ref source, _position);
var slash = Vector128.Create('/');
var slash = Vector128.Create((ushort)'/');
var comparison = AdvSimd.CompareEqual(chunk, slash);

//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Description>Provides an implementation of Ramstack.FileSystem that applies glob-based filtering rules to the underlying file system.</Description>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Description>Provides an implementation of Ramstack.FileSystem using Google Cloud Storage.</Description>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Description>Provides an implementation of Ramstack.FileSystem based on local file system.</Description>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Description>Provides an implementation of Ramstack.FileSystem that adds a specified prefix to the file paths within the underlying file system.</Description>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Description>Provides an implementation of Ramstack.FileSystem that wraps the underlying file system, preventing any destructive operations.</Description>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
2 changes: 1 addition & 1 deletion src/Ramstack.FileSystem.Sub/Ramstack.FileSystem.Sub.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Description>Provides an implementation of Ramstack.FileSystem that wraps an underlying file system for managing files under a specific subpath.</Description>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Ramstack.FileSystem.Amazon;
public class ReadonlyAmazonFileSystemTests : VirtualFileSystemSpecificationTests
{
private readonly TempFileStorage _storage = new TempFileStorage();
private readonly string _storageName = Guid.NewGuid().ToString("N");

[OneTimeSetUp]
public async Task Setup()
Expand Down Expand Up @@ -51,7 +52,7 @@ private AmazonS3FileSystem CreateFileSystem(bool isReadOnly)
ForcePathStyle = true,
};

return new AmazonS3FileSystem(credentials, config, bucketName: "storage")
return new AmazonS3FileSystem(credentials, config, bucketName: _storageName)
{
IsReadOnly = isReadOnly
};
Expand Down
Loading
Loading