Skip to content

Commit a0a4819

Browse files
committed
Attempt to fix the implementation for Windows
1 parent 046881e commit a0a4819

File tree

2 files changed

+45
-3
lines changed

2 files changed

+45
-3
lines changed

src/DotNext.Tests/IO/FileUriTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,11 @@ public static void EncodeAsUriChars(string fileName, string expected)
5353
var uri = new Uri(buffer.Slice(0, charsWritten).ToString(), UriKind.Absolute);
5454
Equal(expected, uri.LocalPath);
5555
}
56+
57+
[Fact]
58+
public static void MaxEncodedLength()
59+
{
60+
const string path = "/some/path";
61+
True(FileUri.GetMaxEncodedLength(path) > path.Length);
62+
}
5663
}

src/DotNext/IO/FileUri.cs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Buffers;
22
using System.Diagnostics;
3+
using System.Runtime.CompilerServices;
34
using System.Text.Encodings.Web;
45
using DotNext.Buffers;
56

@@ -10,7 +11,13 @@ namespace DotNext.IO;
1011
/// </summary>
1112
public static class FileUri
1213
{
13-
private static string FileScheme => OperatingSystem.IsWindows() ? "file:///" : "file://";
14+
// On Windows:
15+
// C:\folder => file:///C:/folder
16+
// \\hostname\folder => file://folder
17+
// \\?\folder => file://?/folder
18+
// \\.\folder => file://./folder
19+
private const string FileScheme = "file://";
20+
private const string UncPrefix = @"\\";
1421

1522
/// <summary>
1623
/// Encodes file name as URI.
@@ -23,7 +30,7 @@ public static Uri Encode(ReadOnlySpan<char> fileName, TextEncoderSettings? setti
2330
{
2431
ThrowIfNotFullyQualified(fileName);
2532
var encoder = settings is null ? UrlEncoder.Default : UrlEncoder.Create(settings);
26-
var maxLength = FileScheme.Length + encoder.MaxOutputCharactersPerInputCharacter * fileName.Length;
33+
var maxLength = GetMaxEncodedLengthCore(fileName, encoder);
2734
using var buffer = (uint)maxLength <= (uint)SpanOwner<char>.StackallocThreshold
2835
? stackalloc char[maxLength]
2936
: new SpanOwner<char>(maxLength);
@@ -32,6 +39,20 @@ public static Uri Encode(ReadOnlySpan<char> fileName, TextEncoderSettings? setti
3239
return new(buffer.Span.Slice(0, writtenCount).ToString(), UriKind.Absolute);
3340
}
3441

42+
/// <summary>
43+
/// Gets the maximum number of characters that can be produced by <see cref="TryEncode"/> method.
44+
/// </summary>
45+
/// <param name="fileName">The file name to be encoded.</param>
46+
/// <param name="encoder">The encoder.</param>
47+
/// <returns>The maximum number of characters that can be produced by the encoder.</returns>
48+
public static int GetMaxEncodedLength(ReadOnlySpan<char> fileName, UrlEncoder? encoder = null)
49+
=> GetMaxEncodedLengthCore(fileName, encoder ?? UrlEncoder.Default);
50+
51+
private static int GetMaxEncodedLengthCore(ReadOnlySpan<char> fileName, UrlEncoder encoder)
52+
=> FileScheme.Length
53+
+ Unsafe.BitCast<bool, byte>(OperatingSystem.IsWindows())
54+
+ encoder.MaxOutputCharactersPerInputCharacter * fileName.Length;
55+
3556
/// <summary>
3657
/// Tries to encode file name as URI.
3758
/// </summary>
@@ -57,9 +78,23 @@ private static void ThrowIfNotFullyQualified(ReadOnlySpan<char> fileName)
5778

5879
private static bool TryEncodeCore(ReadOnlySpan<char> fileName, UrlEncoder encoder, Span<char> output, out int charsWritten)
5980
{
81+
const char slash = '/';
6082
var result = false;
6183
var writer = new SpanWriter<char>(output);
6284
writer.Write(FileScheme);
85+
if (!OperatingSystem.IsWindows())
86+
{
87+
// nothing to do
88+
}
89+
else if (fileName.StartsWith(UncPrefix))
90+
{
91+
fileName = fileName.Slice(UncPrefix.Length);
92+
}
93+
else
94+
{
95+
writer.Add(slash);
96+
}
97+
6398
while (!fileName.IsEmpty)
6499
{
65100
var index = fileName.IndexOf(Path.DirectorySeparatorChar);
@@ -81,7 +116,7 @@ private static bool TryEncodeCore(ReadOnlySpan<char> fileName, UrlEncoder encode
81116

82117
writer.Advance(charsWritten);
83118
if (index >= 0)
84-
writer.Add(Path.DirectorySeparatorChar);
119+
writer.Add(slash);
85120
}
86121

87122
charsWritten = writer.WrittenCount;

0 commit comments

Comments
 (0)