Skip to content

Commit 75440d3

Browse files
authored
Merge pull request #69 from rameel/multitarget-net6-net8
Add net6.0/net8.0 multitargeting with polyfills
2 parents 206ca1d + 811e9fa commit 75440d3

28 files changed

Lines changed: 117 additions & 96 deletions

File tree

.github/workflows/tests.yml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,14 @@ jobs:
2828
10.x
2929
- name: Checkout
3030
uses: actions/checkout@v4
31-
- name: Build
31+
- name: Build (Debug)
3232
run: dotnet build -c Debug
33-
- name: Test
34-
run: dotnet test --no-build --filter "TestCategory!~Cloud"
33+
- name: Build (Release)
34+
run: dotnet build -c Release
35+
- name: Test (Debug)
36+
run: dotnet test -c Debug --no-build --filter "TestCategory!~Cloud"
37+
- name: Test (Release)
38+
run: dotnet test -c Release --no-build --filter "TestCategory!~Cloud"
3539

3640
azure:
3741
name: "Test: AzureFileSystem"
@@ -88,4 +92,4 @@ jobs:
8892
- name: Build
8993
run: dotnet build -c Debug
9094
- name: Test
91-
run: dotnet test --no-build --filter TestCategory=Cloud:Amazon
95+
run: dotnet test --no-build --filter TestCategory=Cloud:Amazon -maxcpucount:1

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<PackageVersion Include="Azure.Storage.Blobs" Version="12.27.0" />
99
<PackageVersion Include="Azure.Storage.Blobs.Batch" Version="12.24.0" />
1010
<PackageVersion Include="Google.Cloud.Storage.V1" Version="4.14.0" />
11-
<PackageVersion Include="Microsoft.Extensions.FileProviders.Abstractions" Version="6.0.1" />
11+
<PackageVersion Include="Microsoft.Extensions.FileProviders.Abstractions" Version="8.0.0" />
1212
<PackageVersion Include="Microsoft.Extensions.FileProviders.Physical" Version="6.0.1" />
1313
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
1414
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />

src/Ramstack.FileSystem.Abstractions/Ramstack.FileSystem.Abstractions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
55
<Description>A .NET library that provides a virtual file system abstraction.</Description>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#if NET6_0
2+
using System.Buffers;
3+
using System.Text;
4+
5+
// ReSharper disable once CheckNamespace
6+
namespace System.IO;
7+
/// <summary>
8+
/// Provides extension methods for the <see cref="StreamReader"/> to offer API compatibility with newer .NET versions.
9+
/// </summary>
10+
public static class StreamReaderExtensions
11+
{
12+
/// <summary>
13+
/// Reads a line of characters asynchronously from the current stream and returns the data as a string.
14+
/// </summary>
15+
/// <param name="reader">The <see cref="StreamReader"/> to read from.</param>
16+
/// <param name="cancellationToken">An optional cancellation token to cancel the operation.</param>
17+
/// <returns>
18+
/// A <see cref="ValueTask{TResult}"/> that represents the asynchronous read operation.
19+
/// The value of the task contains the next line from the stream, or is <see langword="null"/>
20+
/// if the end of the stream is reached.
21+
/// </returns>
22+
public static ValueTask<string?> ReadLineAsync(this StreamReader reader, CancellationToken cancellationToken = default)
23+
{
24+
if (cancellationToken.IsCancellationRequested)
25+
return ValueTask.FromCanceled<string?>(cancellationToken);
26+
27+
return new ValueTask<string?>(reader.ReadLineAsync());
28+
}
29+
30+
/// <summary>
31+
/// Reads all characters from the current position to the end of the stream asynchronously and returns them as one string.
32+
/// </summary>
33+
/// <param name="reader">The <see cref="StreamReader"/> to read from.</param>
34+
/// <param name="cancellationToken">An optional cancellation token to cancel the operation.</param>
35+
/// <returns>
36+
/// A <see cref="Task{TResult}"/> that represents the asynchronous read operation.
37+
/// The value of the task contains a string with the characters from the current position to the end of the stream.
38+
/// </returns>
39+
public static async Task<string> ReadToEndAsync(this StreamReader reader, CancellationToken cancellationToken = default)
40+
{
41+
const int BufferSize = 4096;
42+
43+
var chars = ArrayPool<char>.Shared.Rent(BufferSize);
44+
var sb = new StringBuilder();
45+
46+
while (true)
47+
{
48+
var count = await reader
49+
.ReadAsync(new Memory<char>(chars), cancellationToken)
50+
.ConfigureAwait(false);
51+
52+
if (count == 0)
53+
break;
54+
55+
sb.Append(chars.AsSpan(0, count));
56+
}
57+
58+
ArrayPool<char>.Shared.Return(chars);
59+
return sb.ToString();
60+
}
61+
}
62+
#endif

src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs

Lines changed: 9 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -74,44 +74,11 @@ public static ValueTask<string> ReadAllTextAsync(this VirtualFile file, Cancella
7474
/// </returns>
7575
public static async ValueTask<string> ReadAllTextAsync(this VirtualFile file, Encoding? encoding, CancellationToken cancellationToken = default)
7676
{
77-
//
78-
// Use a manual read loop since .NET 6 lacks a StreamReader.ReadToEndAsync overload that accepts a CancellationToken.
79-
// This ensures the operation remains responsive to cancellation requests.
80-
//
81-
82-
const int BufferSize = 4096;
83-
8477
// ReSharper disable once UseAwaitUsing
8578
using var stream = await file.OpenReadAsync(cancellationToken).ConfigureAwait(false);
86-
var reader = new StreamReader(stream, encoding ??= Encoding.UTF8);
87-
var buffer = (char[]?)null;
88-
89-
try
90-
{
91-
cancellationToken.ThrowIfCancellationRequested();
92-
93-
buffer = ArrayPool<char>.Shared.Rent(encoding.GetMaxCharCount(BufferSize));
94-
var sb = new StringBuilder();
95-
96-
while (true)
97-
{
98-
var count = await reader
99-
.ReadAsync(new Memory<char>(buffer), cancellationToken)
100-
.ConfigureAwait(false);
101-
102-
if (count == 0)
103-
return sb.ToString();
104-
105-
sb.Append(buffer.AsSpan(0, count));
106-
}
107-
}
108-
finally
109-
{
110-
reader.Dispose();
79+
using var reader = new StreamReader(stream, encoding!);
11180

112-
if (buffer is not null)
113-
ArrayPool<char>.Shared.Return(buffer);
114-
}
81+
return await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
11582
}
11683

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

144111
var list = new List<string>();
145-
while (await reader.ReadLineAsync().ConfigureAwait(false) is { } line)
146-
{
147-
cancellationToken.ThrowIfCancellationRequested();
112+
while (await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false) is { } line)
148113
list.Add(line);
149-
}
150114

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

168-
var length = GetStreamLength(stream);
132+
var length = stream.CanSeek ? stream.Length : 0;
169133
if (length > Array.MaxLength)
170134
throw new IOException("The file is too large.");
171135

@@ -179,15 +143,15 @@ static async ValueTask<byte[]> ReadAllBytesImplAsync(Stream stream, Cancellation
179143
{
180144
var bytes = new byte[stream.Length];
181145
var index = 0;
146+
182147
do
183148
{
184149
var count = await stream.ReadAsync(bytes.AsMemory(index), cancellationToken).ConfigureAwait(false);
185150
if (count == 0)
186151
Error_EndOfStream();
187152

188153
index += count;
189-
}
190-
while (index < bytes.Length);
154+
} while (index < bytes.Length);
191155

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

228-
var rented = oldArray;
229-
oldArray = newArray;
230-
231-
ArrayPool<byte>.Shared.Return(rented);
232-
return oldArray;
192+
ArrayPool<byte>.Shared.Return(oldArray);
193+
return newArray;
233194
}
234195
}
235196

236-
static long GetStreamLength(Stream stream)
237-
{
238-
try
239-
{
240-
if (stream.CanSeek)
241-
return stream.Length;
242-
}
243-
catch
244-
{
245-
// skip
246-
}
247-
248-
return 0;
249-
}
250-
251197
static void Error_EndOfStream() =>
252198
throw new EndOfStreamException();
253199
}
@@ -334,7 +280,7 @@ public static async ValueTask WriteAllLinesAsync(this VirtualFile file, IEnumera
334280
await using var writer = new StreamWriter(stream, encoding, bufferSize: -1, leaveOpen: false);
335281

336282
foreach (var line in contents)
337-
await writer.WriteLineAsync(line).ConfigureAwait(false);
283+
await writer.WriteLineAsync(line.AsMemory(), cancellationToken).ConfigureAwait(false);
338284
}
339285

340286
/// <summary>

src/Ramstack.FileSystem.Adapters/Ramstack.FileSystem.Adapters.csproj

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
55
<Description>Provides adapters for integrating Ramstack.FileSystem with Microsoft.Extensions.FileProviders.</Description>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
@@ -43,8 +43,15 @@
4343
</Compile>
4444
</ItemGroup>
4545

46-
<ItemGroup>
46+
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
47+
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" VersionOverride="6.0.1" />
48+
</ItemGroup>
49+
50+
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
4751
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" />
52+
</ItemGroup>
53+
54+
<ItemGroup>
4855
<PackageReference Include="Microsoft.SourceLink.GitHub">
4956
<PrivateAssets>all</PrivateAssets>
5057
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

src/Ramstack.FileSystem.Amazon/Ramstack.FileSystem.Amazon.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
55
<Description>Provides an implementation of Ramstack.FileSystem using Amazon S3 storage.</Description>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>

src/Ramstack.FileSystem.Composite/Ramstack.FileSystem.Composite.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
55
<Description>Provides an implementation of Ramstack.FileSystem that combines multiple file systems into a single composite file system.</Description>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>

src/Ramstack.FileSystem.Globbing/Internal/PathHelper.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ public PathSegmentIterator() =>
249249
if (Avx2.IsSupported && (int)_position + Vector256<ushort>.Count <= length)
250250
{
251251
var chunk = LoadVector256(ref source, _position);
252-
var slash = Vector256.Create('/');
252+
var slash = Vector256.Create((ushort)'/');
253253
var comparison = Avx2.CompareEqual(chunk, slash);
254254

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

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

295295
//

src/Ramstack.FileSystem.Globbing/Ramstack.FileSystem.Globbing.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
55
<Description>Provides an implementation of Ramstack.FileSystem that applies glob-based filtering rules to the underlying file system.</Description>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>

0 commit comments

Comments
 (0)