mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Add PathNormalizer
This commit is contained in:
parent
cb6827e6c2
commit
1c28c08c94
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace LibHac.Common
|
||||
@ -76,6 +77,18 @@ namespace LibHac.Common
|
||||
return new U8String(_buffer.ToArray());
|
||||
}
|
||||
|
||||
public bool IsEmpty() => _buffer.IsEmpty;
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="U8Span"/> has no buffer.
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> if the span has no buffer.
|
||||
/// Otherwise, <see langword="false"/>.</returns>
|
||||
public bool IsNull() => _buffer.IsEmpty;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="U8Span"/> has no buffer or begins with a null terminator.
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> if the span has no buffer or begins with a null terminator.
|
||||
/// Otherwise, <see langword="false"/>.</returns>
|
||||
public bool IsEmpty() => _buffer.IsEmpty || MemoryMarshal.GetReference(_buffer) == 0;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ namespace LibHac.Fs
|
||||
{
|
||||
public static Result CheckMountName(U8Span name)
|
||||
{
|
||||
if (name.IsEmpty()) return ResultFs.NullArgument.Log();
|
||||
if (name.IsNull()) return ResultFs.NullArgument.Log();
|
||||
|
||||
if (name.Length > 0 && name[0] == '@') return ResultFs.InvalidMountName.Log();
|
||||
if (!CheckMountNameImpl(name)) return ResultFs.InvalidMountName.Log();
|
||||
@ -16,7 +16,7 @@ namespace LibHac.Fs
|
||||
|
||||
public static Result CheckMountNameAcceptingReservedMountName(U8Span name)
|
||||
{
|
||||
if (name.IsEmpty()) return ResultFs.NullArgument.Log();
|
||||
if (name.IsNull()) return ResultFs.NullArgument.Log();
|
||||
|
||||
if (!CheckMountNameImpl(name)) return ResultFs.InvalidMountName.Log();
|
||||
|
||||
|
@ -360,7 +360,7 @@ namespace LibHac.Fs
|
||||
}
|
||||
}
|
||||
|
||||
if (path2.IsEmpty() || IsNullTerminator(path2[0]))
|
||||
if (path2.IsEmpty())
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
|
||||
if (ContainsParentDirectoryAlt(path2))
|
||||
|
@ -1,26 +1,30 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
public ref struct PathNormalizer
|
||||
{
|
||||
private U8Span _path;
|
||||
private Result _result;
|
||||
private readonly U8Span _path;
|
||||
public U8Span Path => _path;
|
||||
|
||||
public Result Result { get; }
|
||||
|
||||
public PathNormalizer(U8Span path, Option option)
|
||||
{
|
||||
if (option.HasFlag(Option.AcceptEmpty) && path.IsEmpty())
|
||||
{
|
||||
_path = path;
|
||||
_result = Result.Success;
|
||||
Result = Result.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool preserveUnc = option.HasFlag(Option.PreserveUnc);
|
||||
bool preserveTailSeparator = option.HasFlag(Option.PreserveTailSeparator);
|
||||
bool hasMountName = option.HasFlag(Option.HasMountName);
|
||||
_result = Normalize(out _path, path, preserveUnc, preserveTailSeparator, hasMountName);
|
||||
Result = Normalize(out _path, path, preserveUnc, preserveTailSeparator, hasMountName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,8 +32,33 @@ namespace LibHac.FsService
|
||||
bool preserveTailSeparator, bool hasMountName)
|
||||
{
|
||||
normalizedPath = default;
|
||||
throw new NotImplementedException();
|
||||
|
||||
Result rc = PathTool.IsNormalized(out bool isNormalized, path, preserveUnc, hasMountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (isNormalized)
|
||||
{
|
||||
normalizedPath = path;
|
||||
}
|
||||
else
|
||||
{
|
||||
var buffer = new byte[PathTools.MaxPathLength + 1];
|
||||
|
||||
rc = PathTool.Normalize(buffer, out long normalizedLength, path, preserveUnc, hasMountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// GetLength is capped at MaxPathLength bytes to leave room for the null terminator
|
||||
if (preserveTailSeparator &&
|
||||
PathTool.IsSeparator(path[StringUtils.GetLength(path, PathTools.MaxPathLength) - 1]))
|
||||
{
|
||||
buffer[(int)normalizedLength] = StringTraits.DirectorySeparator;
|
||||
buffer[(int)normalizedLength + 1] = StringTraits.NullTerminator;
|
||||
}
|
||||
|
||||
normalizedPath = new U8Span(buffer);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
@ -148,6 +148,7 @@ void CreateNormalizationTestData(void (*func)(char const*, bool, bool)) {
|
||||
func("mount:/a/b/../c", preserveUnc, true);
|
||||
func("a:/a/b/c", preserveUnc, true);
|
||||
func("mount:/a/b/../c", preserveUnc, true);
|
||||
func("mount:/a/b/../c", preserveUnc, false);
|
||||
func("mount:\\a/b/../c", preserveUnc, true);
|
||||
func("mount:\\a/b\\../c", preserveUnc, true);
|
||||
func("mount:\\a/b/c", preserveUnc, true);
|
||||
|
@ -75,6 +75,7 @@ namespace LibHac.Tests.Fs
|
||||
new object[] {@"./a/b/c/.", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"abc", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/a/b/../c", false, true, @"mount:/a/c", 10, Result.Success},
|
||||
new object[] {@"mount:/a/b/../c", false, false, @"", 0, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"a:/a/b/c", false, true, @"a:/a/b/c", 8, Result.Success},
|
||||
new object[] {@"mount:/a/b/../c", false, true, @"mount:/a/c", 10, Result.Success},
|
||||
new object[] {@"mount:\a/b/../c", false, true, @"mount:\a/c", 10, Result.Success},
|
||||
@ -464,6 +465,7 @@ namespace LibHac.Tests.Fs
|
||||
new object[] {@"./a/b/c/.", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"abc", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"mount:/a/b/../c", false, true, false, Result.Success},
|
||||
new object[] {@"mount:/a/b/../c", false, false, false, ResultFs.InvalidPathFormat.Value},
|
||||
new object[] {@"a:/a/b/c", false, true, true, Result.Success},
|
||||
new object[] {@"mount:/a/b/../c", false, true, false, Result.Success},
|
||||
new object[] {@"mount:\a/b/../c", false, true, false, ResultFs.InvalidPathFormat.Value},
|
||||
|
84
tests/LibHac.Tests/FsService/PathNormalizerTests.cs
Normal file
84
tests/LibHac.Tests/FsService/PathNormalizerTests.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsService;
|
||||
using Xunit;
|
||||
|
||||
namespace LibHac.Tests.FsService
|
||||
{
|
||||
public class PathNormalizerTests
|
||||
{
|
||||
[Fact]
|
||||
public static void Ctor_EmptyPathWithAcceptEmptyOption_ReturnsEmptyPathWithSuccess()
|
||||
{
|
||||
var normalizer = new PathNormalizer("".ToU8Span(), PathNormalizer.Option.AcceptEmpty);
|
||||
|
||||
Assert.Equal(Result.Success, normalizer.Result);
|
||||
Assert.True(normalizer.Path.IsEmpty());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Normalize_PreserveTailSeparatorOption_KeepsExistingTailSeparator()
|
||||
{
|
||||
var normalizer = new PathNormalizer("/a/./b/".ToU8Span(), PathNormalizer.Option.PreserveTailSeparator);
|
||||
|
||||
Assert.Equal(Result.Success, normalizer.Result);
|
||||
Assert.Equal("/a/b/", normalizer.Path.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Normalize_PreserveTailSeparatorOption_IgnoresMissingTailSeparator()
|
||||
{
|
||||
var normalizer = new PathNormalizer("/a/./b".ToU8Span(), PathNormalizer.Option.PreserveTailSeparator);
|
||||
|
||||
Assert.Equal(Result.Success, normalizer.Result);
|
||||
Assert.Equal("/a/b", normalizer.Path.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Normalize_PathAlreadyNormalized_ReturnsSameBuffer()
|
||||
{
|
||||
var originalPath = "/a/b".ToU8Span();
|
||||
var normalizer = new PathNormalizer(originalPath, PathNormalizer.Option.PreserveTailSeparator);
|
||||
|
||||
Assert.Equal(Result.Success, normalizer.Result);
|
||||
|
||||
// Compares addresses and lengths of the buffers
|
||||
Assert.True(originalPath.Value == normalizer.Path.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Normalize_PreserveUncOptionOn_PreservesUncPath()
|
||||
{
|
||||
var normalizer = new PathNormalizer("//aa/bb/..".ToU8Span(), PathNormalizer.Option.PreserveUnc);
|
||||
|
||||
Assert.Equal(Result.Success, normalizer.Result);
|
||||
Assert.Equal(@"\\aa/bb", normalizer.Path.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Normalize_PreserveUncOptionOff_DoesNotPreserveUncPath()
|
||||
{
|
||||
var normalizer = new PathNormalizer("//aa/bb/..".ToU8Span(), PathNormalizer.Option.None);
|
||||
|
||||
Assert.Equal(Result.Success, normalizer.Result);
|
||||
Assert.Equal(@"/aa", normalizer.Path.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Normalize_MountNameOptionOn_ParsesMountName()
|
||||
{
|
||||
var normalizer = new PathNormalizer("mount:/a/./b".ToU8Span(), PathNormalizer.Option.HasMountName);
|
||||
|
||||
Assert.Equal(Result.Success, normalizer.Result);
|
||||
Assert.Equal("mount:/a/b", normalizer.Path.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Normalize_MountNameOptionOff_DoesNotParseMountName()
|
||||
{
|
||||
var normalizer = new PathNormalizer("mount:/a/./b".ToU8Span(), PathNormalizer.Option.None);
|
||||
|
||||
Assert.Equal(ResultFs.InvalidPathFormat.Value, normalizer.Result);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user