Replace hex string converter and move StringUtils

This commit is contained in:
Alex Barney 2020-10-10 20:17:07 -07:00
parent a9632c8d00
commit b5dabe78f5
68 changed files with 394 additions and 187 deletions

View File

@ -3,6 +3,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Util;
namespace LibHac.Bcat.Detail.Service.Core
{

View File

@ -2,7 +2,7 @@
using System.Diagnostics;
using LibHac.Bcat.Detail.Ipc;
using LibHac.Bcat.Detail.Service.Core;
using LibHac.Common;
using LibHac.Util;
namespace LibHac.Bcat.Detail.Service
{

View File

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Util;
namespace LibHac.Bcat
{

View File

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Util;
namespace LibHac.Bcat
{

View File

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Util;
namespace LibHac.Bcat
{

View File

@ -4,6 +4,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Crypto;
using LibHac.Util;
namespace LibHac.Boot
{

View File

@ -7,6 +7,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common.Keys;
using LibHac.Diag;
using LibHac.Util;
namespace LibHac.Boot
{

View File

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Util;
namespace LibHac.Common
{

View File

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using LibHac.Util;
namespace LibHac.Common
{

View File

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using LibHac.Util;
namespace LibHac.Common
{

View File

@ -5,6 +5,7 @@ using System.IO;
using System.Runtime.CompilerServices;
using LibHac.Fs;
using LibHac.Spl;
using LibHac.Util;
namespace LibHac.Common.Keys
{
@ -148,7 +149,7 @@ namespace LibHac.Common.Keys
continue;
}
if (!Utilities.TryToBytes(ctx.CurrentValue, key))
if (!StringUtils.TryFromHexString(ctx.CurrentValue, key))
{
key.Clear();
@ -204,13 +205,13 @@ namespace LibHac.Common.Keys
var rightsId = new RightsId();
var titleKey = new AccessKey();
if (!Utilities.TryToBytes(ctx.CurrentKey, SpanHelpers.AsByteSpan(ref rightsId)))
if (!StringUtils.TryFromHexString(ctx.CurrentKey, SpanHelpers.AsByteSpan(ref rightsId)))
{
logger?.LogMessage($"Invalid rights ID \"{ctx.CurrentKey.ToString()}\" in title key file");
continue;
}
if (!Utilities.TryToBytes(ctx.CurrentValue, SpanHelpers.AsByteSpan(ref titleKey)))
if (!StringUtils.TryFromHexString(ctx.CurrentValue, SpanHelpers.AsByteSpan(ref titleKey)))
{
logger?.LogMessage($"Invalid title key \"{ctx.CurrentValue.ToString()}\" in title key file");
continue;
@ -261,7 +262,7 @@ namespace LibHac.Common.Keys
Delimiter,
Value,
WhiteSpace2,
End,
End
}
private static ReaderStatus GetKeyValuePair(ref KvPairReaderContext reader)

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using LibHac.Fs;
using LibHac.Spl;
using LibHac.Util;
using Type = LibHac.Common.Keys.KeyInfo.KeyType;
using RangeType = LibHac.Common.Keys.KeyInfo.KeyRangeType;

View File

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using LibHac.Diag;
using LibHac.Util;
namespace LibHac.Common.Keys
{
@ -140,7 +141,7 @@ namespace LibHac.Common.Keys
byte index = default;
// Try to get the index of the key name
if (!keyName.Slice(keyName.Length - 2, 2).TryToBytes(SpanHelpers.AsSpan(ref index)))
if (!StringUtils.TryFromHexString(keyName.Slice(keyName.Length - 2, 2), SpanHelpers.AsSpan(ref index)))
return false;
// Check if the index is in this key's range

View File

@ -3,6 +3,7 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Util;
namespace LibHac.Common
{

View File

@ -3,6 +3,7 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using LibHac.Util;
namespace LibHac.Common
{

View File

@ -3,6 +3,7 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using LibHac.Util;
namespace LibHac.Common
{

View File

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Text;
using LibHac.Util;
namespace LibHac.Common
{

View File

@ -3,6 +3,7 @@ using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using LibHac.Util;
namespace LibHac.Common
{

View File

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Text;
using LibHac.Util;
namespace LibHac.Common
{

View File

@ -3,6 +3,7 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Util;
namespace LibHac.Crypto
{

View File

@ -8,6 +8,7 @@ using LibHac.Fs.Fsa;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
using LibHac.Util;
namespace LibHac.Fs
{

View File

@ -4,6 +4,7 @@ using System.IO;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Util;
namespace LibHac.Fs
{

View File

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Util;
namespace LibHac.Fs
{

View File

@ -4,6 +4,7 @@ using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.FsSrv;
using LibHac.FsSystem;
using LibHac.Util;
using static LibHac.Fs.CommonMountNames;
namespace LibHac.Fs.Shim

View File

@ -2,6 +2,7 @@
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.FsSrv;
using LibHac.Util;
namespace LibHac.Fs.Shim
{

View File

@ -5,6 +5,7 @@ using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.FsSrv;
using LibHac.FsSystem;
using LibHac.Util;
using static LibHac.Fs.CommonMountNames;
namespace LibHac.Fs.Shim

View File

@ -10,6 +10,7 @@ using LibHac.FsSystem;
using LibHac.Kvdb;
using LibHac.Ncm;
using LibHac.Spl;
using LibHac.Util;
namespace LibHac.FsSrv
{

View File

@ -9,6 +9,7 @@ using LibHac.FsSystem;
using LibHac.FsSrv.Creators;
using LibHac.FsSystem.NcaUtils;
using LibHac.Spl;
using LibHac.Util;
using RightsId = LibHac.Fs.RightsId;
namespace LibHac.FsSrv

View File

@ -2,6 +2,7 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Util;
namespace LibHac.FsSrv
{

View File

@ -4,6 +4,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Util;
namespace LibHac.FsSrv.Sf
{

View File

@ -2,6 +2,7 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem
{
@ -38,7 +39,7 @@ namespace LibHac.FsSystem
}
else
{
string entryName = Utilities.GetUtf8StringNullTerminated(entry.Name);
string entryName = StringUtils.NullTerminatedUtf8ToString(entry.Name);
entry.Size = GetAesXtsFileSize(PathTools.Combine(Path.ToString(), entryName).ToU8Span());
}
}

View File

@ -3,6 +3,7 @@ using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem
{
@ -63,7 +64,7 @@ namespace LibHac.FsSystem
if (!Mode.HasFlag(OpenDirectoryMode.NoFileSize))
{
string entryName = Utilities.GetUtf8StringNullTerminated(entry.Name);
string entryName = StringUtils.NullTerminatedUtf8ToString(entry.Name);
string entryFullPath = PathTools.Combine(_path.ToString(), entryName);
rc = ParentFileSystem.GetConcatenationFileSize(out long fileSize, entryFullPath.ToU8Span());
@ -122,7 +123,7 @@ namespace LibHac.FsSystem
}
else
{
string name = Utilities.GetUtf8StringNullTerminated(entry.Name);
string name = StringUtils.NullTerminatedUtf8ToString(entry.Name);
var fullPath = PathTools.Combine(_path.ToString(), name).ToU8Span();
return ParentFileSystem.IsConcatenationFile(fullPath);

View File

@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -2,6 +2,7 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -2,6 +2,7 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -5,6 +5,7 @@ using System.IO;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -4,6 +4,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem
{
@ -35,7 +35,7 @@ namespace LibHac.FsSystem
if (!CanReturnEntry(isDir, Mode)) continue;
ReadOnlySpan<byte> name = Utilities.GetUtf8Bytes(localEntry.Name);
ReadOnlySpan<byte> name = StringUtils.StringToUtf8(localEntry.Name);
DirectoryEntryType type = isDir ? DirectoryEntryType.Directory : DirectoryEntryType.File;
long length = isDir ? 0 : ((FileInfo)localEntry).Length;

View File

@ -7,6 +7,7 @@ using LibHac.Common.Keys;
using LibHac.Crypto;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Util;
namespace LibHac.FsSystem.NcaUtils
{

View File

@ -1,9 +1,9 @@
using System;
using System.IO;
using System.Text;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -5,6 +5,7 @@ using LibHac.Crypto;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem.Detail;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSystem.Detail;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -4,6 +4,7 @@ using System.IO.Enumeration;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -1,6 +1,7 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Fs;
using LibHac.Util;
namespace LibHac.FsSystem.RomFs
{
@ -83,7 +84,7 @@ namespace LibHac.FsSystem.RomFs
public bool TryOpenFile(string path, out T fileInfo)
{
FindPathRecursive(Utilities.GetUtf8Bytes(path), out RomEntryKey key);
FindPathRecursive(StringUtils.StringToUtf8(path), out RomEntryKey key);
if (FileTable.TryGetValue(ref key, out RomKeyValuePair<FileRomEntry> keyValuePair))
{
@ -116,7 +117,7 @@ namespace LibHac.FsSystem.RomFs
/// otherwise, <see langword="false"/>.</returns>
public bool TryOpenDirectory(string path, out FindPosition position)
{
FindPathRecursive(Utilities.GetUtf8Bytes(path), out RomEntryKey key);
FindPathRecursive(StringUtils.StringToUtf8(path), out RomEntryKey key);
if (DirectoryTable.TryGetValue(ref key, out RomKeyValuePair<DirectoryRomEntry> keyValuePair))
{
@ -169,7 +170,7 @@ namespace LibHac.FsSystem.RomFs
position.NextFile = entry.NextSibling;
info = entry.Info;
name = Utilities.GetUtf8String(nameBytes);
name = StringUtils.Utf8ToString(nameBytes);
return true;
}
@ -193,7 +194,7 @@ namespace LibHac.FsSystem.RomFs
ref DirectoryRomEntry entry = ref DirectoryTable.GetValueReference(position.NextDirectory, out Span<byte> nameBytes);
position.NextDirectory = entry.NextSibling;
name = Utilities.GetUtf8String(nameBytes);
name = StringUtils.Utf8ToString(nameBytes);
return true;
}
@ -207,7 +208,7 @@ namespace LibHac.FsSystem.RomFs
public void AddFile(string path, ref T fileInfo)
{
path = PathTools.Normalize(path);
ReadOnlySpan<byte> pathBytes = Utilities.GetUtf8Bytes(path);
ReadOnlySpan<byte> pathBytes = StringUtils.StringToUtf8(path);
if (path == "/") throw new ArgumentException("Path cannot be empty");
@ -223,7 +224,7 @@ namespace LibHac.FsSystem.RomFs
{
path = PathTools.Normalize(path);
CreateDirectoryRecursive(Utilities.GetUtf8Bytes(path));
CreateDirectoryRecursive(StringUtils.StringToUtf8(path));
}
/// <summary>

View File

@ -1,8 +1,8 @@
using System;
using System.Text;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem.RomFs
{

View File

@ -3,6 +3,7 @@ using System.IO;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Util;
namespace LibHac.FsSystem.Save
{
@ -59,7 +60,7 @@ namespace LibHac.FsSystem.Save
position.NextFile = entry.NextSibling;
info = entry.Value;
name = Utilities.GetUtf8StringNullTerminated(nameBytes);
name = StringUtils.NullTerminatedUtf8ToString(nameBytes);
return true;
}
@ -85,7 +86,7 @@ namespace LibHac.FsSystem.Save
position.NextDirectory = entry.NextSibling;
name = Utilities.GetUtf8StringNullTerminated(nameBytes);
name = StringUtils.NullTerminatedUtf8ToString(nameBytes);
return true;
}

View File

@ -1,8 +1,8 @@
using System;
using System.Text;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem.Save
{

View File

@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Util;
namespace LibHac.FsSystem.Save
{

View File

@ -3,6 +3,7 @@ using System.Diagnostics;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -5,6 +5,7 @@ using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem
{

View File

@ -2,6 +2,7 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Util;
namespace LibHac.Kvdb
{

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Util;
namespace LibHac
{

View File

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using LibHac.Common;
using LibHac.Util;
namespace LibHac.Sm
{

View File

@ -12,6 +12,7 @@ using LibHac.FsSystem.NcaUtils;
using LibHac.FsSystem.Save;
using LibHac.Ncm;
using LibHac.Ns;
using LibHac.Util;
namespace LibHac
{

View File

@ -0,0 +1,233 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace LibHac.Util.Impl
{
internal static class HexConverter
{
public enum Casing : uint
{
// Output [ '0' .. '9' ] and [ 'A' .. 'F' ].
Upper = 0,
// Output [ '0' .. '9' ] and [ 'a' .. 'f' ].
// This works because values in the range [ 0x30 .. 0x39 ] ([ '0' .. '9' ])
// already have the 0x20 bit set, so ORing them with 0x20 is a no-op,
// while outputs in the range [ 0x41 .. 0x46 ] ([ 'A' .. 'F' ])
// don't have the 0x20 bit set, so ORing them maps to
// [ 0x61 .. 0x66 ] ([ 'a' .. 'f' ]), which is what we want.
Lower = 0x2020U,
}
// We want to pack the incoming byte into a single integer [ 0000 HHHH 0000 LLLL ],
// where HHHH and LLLL are the high and low nibbles of the incoming byte. Then
// subtract this integer from a constant minuend as shown below.
//
// [ 1000 1001 1000 1001 ]
// - [ 0000 HHHH 0000 LLLL ]
// =========================
// [ *YYY **** *ZZZ **** ]
//
// The end result of this is that YYY is 0b000 if HHHH <= 9, and YYY is 0b111 if HHHH >= 10.
// Similarly, ZZZ is 0b000 if LLLL <= 9, and ZZZ is 0b111 if LLLL >= 10.
// (We don't care about the value of asterisked bits.)
//
// To turn a nibble in the range [ 0 .. 9 ] into hex, we calculate hex := nibble + 48 (ascii '0').
// To turn a nibble in the range [ 10 .. 15 ] into hex, we calculate hex := nibble - 10 + 65 (ascii 'A').
// => hex := nibble + 55.
// The difference in the starting ASCII offset is (55 - 48) = 7, depending on whether the nibble is <= 9 or >= 10.
// Since 7 is 0b111, this conveniently matches the YYY or ZZZ value computed during the earlier subtraction.
// The commented out code below is code that directly implements the logic described above.
// uint packedOriginalValues = (((uint)value & 0xF0U) << 4) + ((uint)value & 0x0FU);
// uint difference = 0x8989U - packedOriginalValues;
// uint add7Mask = (difference & 0x7070U) >> 4; // line YYY and ZZZ back up with the packed values
// uint packedResult = packedOriginalValues + add7Mask + 0x3030U /* ascii '0' */;
// The code below is equivalent to the commented out code above but has been tweaked
// to allow codegen to make some extra optimizations.
// The low byte of the packed result contains the hex representation of the incoming byte's low nibble.
// The adjacent byte of the packed result contains the hex representation of the incoming byte's high nibble.
// Finally, write to the output buffer starting with the *highest* index so that codegen can
// elide all but the first bounds check. (This only works if 'startingIndex' is a compile-time constant.)
// The JIT can elide bounds checks if 'startingIndex' is constant and if the caller is
// writing to a span of known length (or the caller has already checked the bounds of the
// furthest access).
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ToBytesBuffer(byte value, Span<byte> buffer, int startingIndex = 0, Casing casing = Casing.Upper)
{
uint difference = ((value & 0xF0U) << 4) + (value & 0x0FU) - 0x8989U;
uint packedResult = ((((uint)(-(int)difference) & 0x7070U) >> 4) + difference + 0xB9B9U) | (uint)casing;
buffer[startingIndex + 1] = (byte)packedResult;
buffer[startingIndex] = (byte)(packedResult >> 8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ToCharsBuffer(byte value, Span<char> buffer, int startingIndex = 0, Casing casing = Casing.Upper)
{
uint difference = ((value & 0xF0U) << 4) + (value & 0x0FU) - 0x8989U;
uint packedResult = ((((uint)(-(int)difference) & 0x7070U) >> 4) + difference + 0xB9B9U) | (uint)casing;
buffer[startingIndex + 1] = (char)(packedResult & 0xFF);
buffer[startingIndex] = (char)(packedResult >> 8);
}
public static void EncodeToUtf16(ReadOnlySpan<byte> bytes, Span<char> chars, Casing casing = Casing.Upper)
{
Debug.Assert(chars.Length >= bytes.Length * 2);
for (int pos = 0; pos < bytes.Length; ++pos)
{
ToCharsBuffer(bytes[pos], chars, pos * 2, casing);
}
}
public static unsafe string ToString(ReadOnlySpan<byte> bytes, Casing casing = Casing.Upper)
{
fixed (byte* bytesPtr = bytes)
{
// Todo: Make lambda static in C# 9
return string.Create(bytes.Length * 2, (Ptr: (IntPtr)bytesPtr, bytes.Length, casing), (chars, args) =>
{
var ros = new ReadOnlySpan<byte>((byte*)args.Ptr, args.Length);
EncodeToUtf16(ros, chars, args.casing);
});
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static char ToCharUpper(int value)
{
value &= 0xF;
value += '0';
if (value > '9')
{
value += ('A' - ('9' + 1));
}
return (char)value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static char ToCharLower(int value)
{
value &= 0xF;
value += '0';
if (value > '9')
{
value += ('a' - ('9' + 1));
}
return (char)value;
}
public static bool TryDecodeFromUtf16(ReadOnlySpan<char> chars, Span<byte> bytes)
{
return TryDecodeFromUtf16(chars, bytes, out _);
}
public static bool TryDecodeFromUtf16(ReadOnlySpan<char> chars, Span<byte> bytes, out int charsProcessed)
{
Debug.Assert(chars.Length % 2 == 0, "Un-even number of characters provided");
Debug.Assert(chars.Length / 2 == bytes.Length, "Target buffer not right-sized for provided characters");
int i = 0;
int j = 0;
int byteLo = 0;
int byteHi = 0;
while (j < bytes.Length)
{
byteLo = FromChar(chars[i + 1]);
byteHi = FromChar(chars[i]);
// byteHi hasn't been shifted to the high half yet, so the only way the bitwise or produces this pattern
// is if either byteHi or byteLo was not a hex character.
if ((byteLo | byteHi) == 0xFF)
break;
bytes[j++] = (byte)((byteHi << 4) | byteLo);
i += 2;
}
if (byteLo == 0xFF)
i++;
charsProcessed = i;
return (byteLo | byteHi) != 0xFF;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int FromChar(int c)
{
return c >= CharToHexLookup.Length ? 0xFF : CharToHexLookup[c];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int FromUpperChar(int c)
{
return c > 71 ? 0xFF : CharToHexLookup[c];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int FromLowerChar(int c)
{
if ((uint)(c - '0') <= '9' - '0')
return c - '0';
if ((uint)(c - 'a') <= 'f' - 'a')
return c - 'a' + 10;
return 0xFF;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsHexChar(int c)
{
return FromChar(c) != 0xFF;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsHexUpperChar(int c)
{
return (uint)(c - '0') <= 9 || (uint)(c - 'A') <= ('F' - 'A');
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsHexLowerChar(int c)
{
return (uint)(c - '0') <= 9 || (uint)(c - 'a') <= ('f' - 'a');
}
/// <summary>Map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.</summary>
public static ReadOnlySpan<byte> CharToHexLookup => new byte[]
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63
0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95
0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 111
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 127
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 143
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 159
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 175
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 191
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 207
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 223
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 239
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 255
};
}
}

View File

@ -1,7 +1,8 @@
using System;
using System.Text;
using LibHac.Util.Impl;
namespace LibHac.Common
namespace LibHac.Util
{
public static class StringUtils
{
@ -159,11 +160,23 @@ namespace LibHac.Common
return iDest;
}
public static ReadOnlySpan<byte> StringToUtf8(string value)
{
return Encoding.UTF8.GetBytes(value).AsSpan();
}
public static string Utf8ToString(ReadOnlySpan<byte> value)
{
return Encoding.UTF8.GetString(value);
}
public static string NullTerminatedUtf8ToString(ReadOnlySpan<byte> value)
{
int length = GetLength(value);
return Encoding.UTF8.GetString(value.Slice(0, length));
}
public static string Utf8ZToString(ReadOnlySpan<byte> value)
{
return Utf8ToString(value.Slice(0, GetLength(value)));
@ -184,5 +197,69 @@ namespace LibHac.Common
return (uint)(c - (byte)'0') <= 9 ||
(c | 0x20u) - (byte)'a' <= 'f' - 'a';
}
public static bool TryFromHexString(ReadOnlySpan<char> chars, Span<byte> outputBytes)
{
if ((uint)chars.Length % 2 != 0)
return false;
uint bytesLength = (uint)chars.Length / 2;
if ((uint)outputBytes.Length >= bytesLength)
{
Span<byte> bytes = outputBytes.Slice(0, (int)bytesLength);
return HexConverter.TryDecodeFromUtf16(chars, bytes);
}
return false;
}
public static byte[] ToBytes(this string input)
{
return FromHexString(input);
}
public static byte[] FromHexString(string input)
{
if ((uint)input.Length % 2 != 0)
throw new FormatException("Hex input must be a multiple of 2.");
var result = new byte[input.Length >> 1];
if (!HexConverter.TryDecodeFromUtf16(input, result))
{
throw new FormatException("Hex input contains invalid characters.");
}
return result;
}
public static void FromHexString(ReadOnlySpan<char> chars, Span<byte> outputBytes)
{
if ((uint)chars.Length % 2 != 0)
throw new FormatException("Hex input must be a multiple of 2.");
uint bytesLength = (uint)chars.Length / 2;
if ((uint)outputBytes.Length >= bytesLength)
{
Span<byte> bytes = outputBytes.Slice(0, (int)bytesLength);
if (!HexConverter.TryDecodeFromUtf16(chars, bytes))
{
throw new FormatException("Hex input contains invalid characters.");
}
}
throw new ArgumentException("Buffer is not large enough to fit the input hex string.", nameof(outputBytes));
}
public static string ToHexString(this byte[] bytes) => ToHexString((ReadOnlySpan<byte>)bytes);
public static string ToHexString(this Span<byte> bytes) => ToHexString((ReadOnlySpan<byte>)bytes);
public static string ToHexString(this ReadOnlySpan<byte> bytes)
{
return HexConverter.ToString(bytes);
}
}
}

View File

@ -67,27 +67,6 @@ namespace LibHac
return a1.SequenceEqual(a2);
}
public static ReadOnlySpan<byte> GetUtf8Bytes(string value)
{
return Encoding.UTF8.GetBytes(value).AsSpan();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string GetUtf8String(ReadOnlySpan<byte> value)
{
return Encoding.UTF8.GetString(value);
}
public static string GetUtf8StringNullTerminated(ReadOnlySpan<byte> value)
{
int i;
for (i = 0; i < value.Length && value[i] != 0; i++) { }
value = value.Slice(0, i);
return Encoding.UTF8.GetString(value);
}
public static bool IsEmpty(this byte[] array) => ((ReadOnlySpan<byte>)array).IsEmpty();
public static bool IsEmpty(this Span<byte> span) => ((ReadOnlySpan<byte>)span).IsEmpty();
@ -234,147 +213,6 @@ namespace LibHac
return Encoding.UTF8.GetString(reader.ReadBytes(size), 0, size);
}
private static bool TryHexToInt(char c, out int value)
{
switch (c)
{
case '0':
value = 0; break;
case '1':
value = 1; break;
case '2':
value = 2; break;
case '3':
value = 3; break;
case '4':
value = 4; break;
case '5':
value = 5; break;
case '6':
value = 6; break;
case '7':
value = 7; break;
case '8':
value = 8; break;
case '9':
value = 9; break;
case 'a':
case 'A':
value = 10; break;
case 'b':
case 'B':
value = 11; break;
case 'c':
case 'C':
value = 12; break;
case 'd':
case 'D':
value = 13; break;
case 'e':
case 'E':
value = 14; break;
case 'f':
case 'F':
value = 15; break;
default:
value = 0;
return false;
}
return true;
}
private static readonly byte[,] ByteLookup = {
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
{0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0}
};
public static byte[] ToBytes(this string input)
{
var result = new byte[(input.Length + 1) >> 1];
int lastcell = result.Length - 1;
int lastchar = input.Length - 1;
for (int i = 0; i < input.Length; i++)
{
if (!TryHexToInt(input[lastchar - i], out int hexInt))
{
throw new FormatException($"Unrecognized hex char {input[lastchar - i]}");
}
result[lastcell - (i >> 1)] |= ByteLookup[i & 1, hexInt];
}
return result;
}
public static bool TryToBytes(this string input, out byte[] bytes)
{
var result = new byte[(input.Length + 1) >> 1];
int lastcell = result.Length - 1;
int lastchar = input.Length - 1;
for (int i = 0; i < input.Length; i++)
{
if (!TryHexToInt(input[lastchar - i], out int hexInt))
{
bytes = null;
return false;
}
result[lastcell - (i >> 1)] |= ByteLookup[i & 1, hexInt];
}
bytes = result;
return true;
}
public static bool TryToBytes(this ReadOnlySpan<char> input, Span<byte> output)
{
if (input.Length != output.Length * 2)
return false;
int lastcell = output.Length - 1;
int lastchar = input.Length - 1;
for (int i = 0; i < input.Length; i++)
{
if (!TryHexToInt(input[lastchar - i], out int hexInt))
{
return false;
}
output[lastcell - (i >> 1)] |= ByteLookup[i & 1, hexInt];
}
return true;
}
private static readonly uint[] Lookup32 = CreateLookup32();
private static uint[] CreateLookup32()
{
var result = new uint[256];
for (int i = 0; i < 256; i++)
{
string s = i.ToString("X2");
result[i] = s[0] + ((uint)s[1] << 16);
}
return result;
}
public static string ToHexString(this byte[] bytes) => ToHexString(bytes.AsSpan());
public static string ToHexString(this Span<byte> bytes) => ToHexString((ReadOnlySpan<byte>)bytes);
public static string ToHexString(this ReadOnlySpan<byte> bytes)
{
uint[] lookup32 = Lookup32;
var result = new char[bytes.Length * 2];
for (int i = 0; i < bytes.Length; i++)
{
uint val = lookup32[bytes[i]];
result[2 * i] = (char)val;
result[2 * i + 1] = (char)(val >> 16);
}
return new string(result);
}
public static long MediaToReal(long media)
{
return MediaSize * media;

View File

@ -6,6 +6,7 @@ using LibHac.Common;
using LibHac.Crypto;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Util;
using static hactoolnet.Print;
namespace hactoolnet
@ -67,7 +68,7 @@ namespace hactoolnet
AesXtsFileHeader header = xtsFile.Header;
uint magic = header.Magic;
PrintItem(sb, colLen, " Magic:", Utilities.GetUtf8String(SpanHelpers.AsReadOnlyByteSpan(in magic)));
PrintItem(sb, colLen, " Magic:", StringUtils.Utf8ToString(SpanHelpers.AsReadOnlyByteSpan(in magic)));
PrintItem(sb, colLen, " Content Type:", GetContentType(contentType));
PrintItem(sb, colLen, " Content Size:", $"{header.Size:x12}");
PrintItem(sb, colLen, " Header HMAC:", header.Signature);

View File

@ -4,6 +4,7 @@ using System.Text;
using LibHac;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Util;
using static hactoolnet.Print;
namespace hactoolnet

View File

@ -4,6 +4,7 @@ using System.Text;
using LibHac;
using LibHac.Common.Keys;
using LibHac.Fs;
using LibHac.Util;
namespace hactoolnet
{

View File

@ -1,5 +1,6 @@
using System;
using LibHac.Crypto;
using LibHac.Util;
using Xunit;
namespace LibHac.Tests

View File

@ -1,5 +1,6 @@
using System.Linq;
using LibHac.FsSystem;
using LibHac.Util;
using Xunit;
namespace LibHac.Tests

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using LibHac.Util;
using Xunit;
namespace LibHac.Tests.CryptoTests

View File

@ -2,6 +2,7 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Util;
using Xunit;
namespace LibHac.Tests.Fs.IFileSystemTestBase

View File

@ -3,6 +3,7 @@ using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Util;
using Xunit;
namespace LibHac.Tests.Fs

View File

@ -1,5 +1,6 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Util;
using Xunit;
namespace LibHac.Tests.Fs

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using LibHac.Common;
using LibHac.FsSystem;
using LibHac.Util;
using Xunit;
namespace LibHac.Tests