mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add Utf8StringUtil and update MountUtility for 13.1.0
This commit is contained in:
parent
123fc0655d
commit
d412c15387
2 changed files with 200 additions and 7 deletions
|
@ -1,16 +1,35 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.Os;
|
||||
using LibHac.Util;
|
||||
using static LibHac.Fs.StringTraits;
|
||||
using static LibHac.Fs.Impl.AccessLogStrings;
|
||||
using static LibHac.Fs.StringTraits;
|
||||
|
||||
namespace LibHac.Fs.Fsa;
|
||||
|
||||
/// <summary>
|
||||
/// Contains functions for managing mounted file systems.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public static class MountUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the mount name and non-mounted path components from a path that has a mount name.
|
||||
/// </summary>
|
||||
/// <param name="mountName">If the method returns successfully, contains the mount name of the provided path;
|
||||
/// otherwise the contents are undefined.</param>
|
||||
/// <param name="subPath">If the method returns successfully, contains the provided path without the
|
||||
/// mount name; otherwise the contents are undefined.</param>
|
||||
/// <param name="path">The <see cref="Path"/> to process.</param>
|
||||
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||
/// <see cref="ResultFs.InvalidPathFormat"/>: <paramref name="path"/> does not contain a sub path after
|
||||
/// the mount name that begins with <c>/</c> or <c>\</c>.<br/>
|
||||
/// <see cref="ResultFs.InvalidMountName"/>: <paramref name="path"/> contains an invalid mount name
|
||||
/// or does not have a mount name.</returns>
|
||||
private static Result GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, U8Span path)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out mountName);
|
||||
|
@ -82,8 +101,7 @@ public static class MountUtility
|
|||
return false;
|
||||
}
|
||||
|
||||
// Todo: VerifyUtf8String
|
||||
return true;
|
||||
return Utf8StringUtil.VerifyUtf8String(name);
|
||||
}
|
||||
|
||||
public static bool IsUsedReservedMountName(this FileSystemClientImpl fs, U8Span name)
|
||||
|
@ -144,7 +162,12 @@ public static class MountUtility
|
|||
|
||||
if (fileSystem.IsFileDataCacheAttachable())
|
||||
{
|
||||
// Todo: Data cache purge
|
||||
using var fileDataCacheAccessor = new GlobalFileDataCacheAccessorReadableScopedPointer();
|
||||
|
||||
if (fs.TryGetGlobalFileDataCacheAccessor(ref Unsafe.AsRef(in fileDataCacheAccessor)))
|
||||
{
|
||||
fileSystem.PurgeFileDataCache(fileDataCacheAccessor.Get());
|
||||
}
|
||||
}
|
||||
|
||||
fs.Unregister(mountName);
|
||||
|
@ -226,13 +249,23 @@ public static class MountUtility
|
|||
public static Result ConvertToFsCommonPath(this FileSystemClient fs, U8SpanMutable commonPathBuffer,
|
||||
U8Span path)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (commonPathBuffer.IsNull())
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
{
|
||||
rc = ResultFs.NullptrArgument.Value;
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (path.IsNull())
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
{
|
||||
rc = ResultFs.NullptrArgument.Value;
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result rc = GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, path);
|
||||
rc = GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, path);
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
|
|
160
src/LibHac/Util/Utf8StringUtil.cs
Normal file
160
src/LibHac/Util/Utf8StringUtil.cs
Normal file
|
@ -0,0 +1,160 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
|
||||
namespace LibHac.Util;
|
||||
|
||||
/// <summary>
|
||||
/// Contains functions for verifying and copying UTF-8 strings.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 13.4.0</remarks>
|
||||
public static class Utf8StringUtil
|
||||
{
|
||||
private static ReadOnlySpan<byte> CodePointByteLengthTable => new byte[]
|
||||
{
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
public static bool VerifyUtf8String(U8Span str)
|
||||
{
|
||||
return GetCodePointCountOfUtf8String(str) != -1;
|
||||
}
|
||||
|
||||
public static int GetCodePointCountOfUtf8String(U8Span str)
|
||||
{
|
||||
Assert.SdkRequiresGreater(str.Length, 0);
|
||||
|
||||
ReadOnlySpan<byte> currentStr = str.Value;
|
||||
int codePointCount = 0;
|
||||
|
||||
while (currentStr.Length != 0)
|
||||
{
|
||||
int codePointByteLength = GetCodePointByteLength(currentStr[0]);
|
||||
|
||||
if (codePointByteLength > currentStr.Length)
|
||||
return -1;
|
||||
|
||||
if (!VerifyCode(currentStr.Slice(0, codePointByteLength)))
|
||||
return -1;
|
||||
|
||||
currentStr = currentStr.Slice(codePointByteLength);
|
||||
|
||||
codePointCount++;
|
||||
}
|
||||
|
||||
return codePointCount;
|
||||
}
|
||||
|
||||
public static int CopyUtf8String(Span<byte> output, ReadOnlySpan<byte> input, int maxCount)
|
||||
{
|
||||
Assert.SdkRequiresGreater(output.Length, 0);
|
||||
Assert.SdkRequiresGreater(input.Length, 0);
|
||||
Assert.SdkRequiresGreater(maxCount, 0);
|
||||
|
||||
ReadOnlySpan<byte> currentInput = input;
|
||||
int remainingCount = maxCount;
|
||||
|
||||
while (remainingCount > 0 && currentInput.Length != 0)
|
||||
{
|
||||
// Verify the current code point
|
||||
int codePointLength = GetCodePointByteLength(currentInput[0]);
|
||||
if (codePointLength > currentInput.Length)
|
||||
break;
|
||||
|
||||
if (!VerifyCode(currentInput.Slice(0, codePointLength)))
|
||||
break;
|
||||
|
||||
// Ensure the output is large enough to hold the additional code point
|
||||
int currentOutputLength =
|
||||
Unsafe.ByteOffset(ref MemoryMarshal.GetReference(input), ref MemoryMarshal.GetReference(currentInput))
|
||||
.ToInt32() + codePointLength;
|
||||
|
||||
if (currentOutputLength + 1 > output.Length)
|
||||
break;
|
||||
|
||||
// Advance to the next code point
|
||||
currentInput = currentInput.Slice(codePointLength);
|
||||
remainingCount--;
|
||||
}
|
||||
|
||||
// Copy the valid UTF-8 to the output buffer
|
||||
int byteLength = Unsafe
|
||||
.ByteOffset(ref MemoryMarshal.GetReference(input), ref MemoryMarshal.GetReference(currentInput)).ToInt32();
|
||||
|
||||
Assert.SdkAssert(byteLength + 1 <= output.Length);
|
||||
|
||||
if (byteLength != 0)
|
||||
input.Slice(0, byteLength).CopyTo(output);
|
||||
|
||||
output[byteLength] = 0;
|
||||
return byteLength;
|
||||
}
|
||||
|
||||
private static int GetCodePointByteLength(byte head)
|
||||
{
|
||||
return CodePointByteLengthTable[head];
|
||||
}
|
||||
|
||||
private static bool IsValidTail(byte tail)
|
||||
{
|
||||
return (tail & 0xC0) == 0x80;
|
||||
}
|
||||
|
||||
private static bool VerifyCode(ReadOnlySpan<byte> str)
|
||||
{
|
||||
if (str.Length == 1)
|
||||
return true;
|
||||
|
||||
switch (str.Length)
|
||||
{
|
||||
case 2:
|
||||
if (!IsValidTail(str[1]))
|
||||
return false;
|
||||
|
||||
break;
|
||||
case 3:
|
||||
if (str[0] == 0xE0 && (str[1] & 0x20) == 0)
|
||||
return false;
|
||||
|
||||
if (str[0] == 0xED && (str[1] & 0x20) != 0)
|
||||
return false;
|
||||
|
||||
if (!IsValidTail(str[1]) || !IsValidTail(str[2]))
|
||||
return false;
|
||||
|
||||
break;
|
||||
case 4:
|
||||
if (str[0] == 0xF0 && (str[1] & 0x30) == 0)
|
||||
return false;
|
||||
|
||||
if (str[0] == 0xFD && (str[1] & 0x30) != 0)
|
||||
return false;
|
||||
|
||||
if (!IsValidTail(str[1]) || !IsValidTail(str[2]) || !IsValidTail(str[3]))
|
||||
return false;
|
||||
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue