diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Common/Utilities | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Common/Utilities')
| -rw-r--r-- | src/Ryujinx.Common/Utilities/BitUtils.cs | 60 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/BitfieldExtensions.cs | 57 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/Buffers.cs | 59 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/CommonJsonContext.cs | 11 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/EmbeddedResources.cs | 148 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/HexUtils.cs | 90 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/JsonHelper.cs | 98 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs | 302 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/NetworkHelpers.cs | 66 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/SpanHelpers.cs | 61 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/StreamUtils.cs | 31 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs | 34 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Utilities/UInt128Utils.cs | 18 |
13 files changed, 1035 insertions, 0 deletions
diff --git a/src/Ryujinx.Common/Utilities/BitUtils.cs b/src/Ryujinx.Common/Utilities/BitUtils.cs new file mode 100644 index 00000000..f885ce84 --- /dev/null +++ b/src/Ryujinx.Common/Utilities/BitUtils.cs @@ -0,0 +1,60 @@ +using System; +using System.Numerics; + +namespace Ryujinx.Common +{ + public static class BitUtils + { + public static T AlignUp<T>(T value, T size) + where T : IBinaryInteger<T> + { + return (value + (size - T.One)) & -size; + } + + public static T AlignDown<T>(T value, T size) + where T : IBinaryInteger<T> + { + return value & -size; + } + + public static T DivRoundUp<T>(T value, T dividend) + where T : IBinaryInteger<T> + { + return (value + (dividend - T.One)) / dividend; + } + + public static int Pow2RoundUp(int value) + { + value--; + + value |= (value >> 1); + value |= (value >> 2); + value |= (value >> 4); + value |= (value >> 8); + value |= (value >> 16); + + return ++value; + } + + public static int Pow2RoundDown(int value) + { + return BitOperations.IsPow2(value) ? value : Pow2RoundUp(value) >> 1; + } + + public static long ReverseBits64(long value) + { + return (long)ReverseBits64((ulong)value); + } + + private static ulong ReverseBits64(ulong value) + { + value = ((value & 0xaaaaaaaaaaaaaaaa) >> 1 ) | ((value & 0x5555555555555555) << 1 ); + value = ((value & 0xcccccccccccccccc) >> 2 ) | ((value & 0x3333333333333333) << 2 ); + value = ((value & 0xf0f0f0f0f0f0f0f0) >> 4 ) | ((value & 0x0f0f0f0f0f0f0f0f) << 4 ); + value = ((value & 0xff00ff00ff00ff00) >> 8 ) | ((value & 0x00ff00ff00ff00ff) << 8 ); + value = ((value & 0xffff0000ffff0000) >> 16) | ((value & 0x0000ffff0000ffff) << 16); + + return (value >> 32) | (value << 32); + } + } +} diff --git a/src/Ryujinx.Common/Utilities/BitfieldExtensions.cs b/src/Ryujinx.Common/Utilities/BitfieldExtensions.cs new file mode 100644 index 00000000..ca429944 --- /dev/null +++ b/src/Ryujinx.Common/Utilities/BitfieldExtensions.cs @@ -0,0 +1,57 @@ +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Common.Utilities +{ + public static class BitfieldExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Extract<T>(this T value, int lsb) where T : IBinaryInteger<T> + { + int bitSize = Unsafe.SizeOf<T>() * 8; + lsb &= bitSize - 1; + + return !T.IsZero((value >>> lsb) & T.One); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Extract<T>(this T value, int lsb, int length) where T : IBinaryInteger<T> + { + int bitSize = Unsafe.SizeOf<T>() * 8; + lsb &= bitSize - 1; + + return (value >>> lsb) & (~T.Zero >>> (bitSize - length)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ExtractSx<T>(this T value, int lsb, int length) where T : IBinaryInteger<T> + { + int bitSize = Unsafe.SizeOf<T>() * 8; + int shift = lsb & (bitSize - 1); + + return (value << (bitSize - (shift + length))) >> (bitSize - length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Insert<T>(this T value, int lsb, bool toInsert) where T : IBinaryInteger<T> + { + int bitSize = Unsafe.SizeOf<T>() * 8; + lsb &= bitSize - 1; + + T mask = T.One << lsb; + + return (value & ~mask) | (toInsert ? mask : T.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Insert<T>(this T value, int lsb, int length, T toInsert) where T : IBinaryInteger<T> + { + int bitSize = Unsafe.SizeOf<T>() * 8; + lsb &= bitSize - 1; + + T mask = (~T.Zero >>> (bitSize - length)) << lsb; + + return (value & ~mask) | ((toInsert << lsb) & mask); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Utilities/Buffers.cs b/src/Ryujinx.Common/Utilities/Buffers.cs new file mode 100644 index 00000000..d614bfc4 --- /dev/null +++ b/src/Ryujinx.Common/Utilities/Buffers.cs @@ -0,0 +1,59 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.Utilities +{ + [DebuggerDisplay("{ToString()}")] + [StructLayout(LayoutKind.Sequential, Size = 16)] + public struct Buffer16 + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1; + + public byte this[int i] + { + get => Bytes[i]; + set => Bytes[i] = value; + } + + public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this); + + // Prevent a defensive copy by changing the read-only in reference to a reference with Unsafe.AsRef() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Span<byte>(in Buffer16 value) + { + return SpanHelpers.AsByteSpan(ref Unsafe.AsRef(in value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan<byte>(in Buffer16 value) + { + return SpanHelpers.AsReadOnlyByteSpan(ref Unsafe.AsRef(in value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T As<T>() where T : unmanaged + { + if (Unsafe.SizeOf<T>() > (uint)Unsafe.SizeOf<Buffer16>()) + { + throw new ArgumentException(); + } + + return ref MemoryMarshal.GetReference(AsSpan<T>()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span<T> AsSpan<T>() where T : unmanaged + { + return SpanHelpers.AsSpan<Buffer16, T>(ref this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly ReadOnlySpan<T> AsReadOnlySpan<T>() where T : unmanaged + { + return SpanHelpers.AsReadOnlySpan<Buffer16, T>(ref Unsafe.AsRef(in this)); + } + } +} diff --git a/src/Ryujinx.Common/Utilities/CommonJsonContext.cs b/src/Ryujinx.Common/Utilities/CommonJsonContext.cs new file mode 100644 index 00000000..d7b3f78c --- /dev/null +++ b/src/Ryujinx.Common/Utilities/CommonJsonContext.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Utilities +{ + [JsonSerializable(typeof(string[]), TypeInfoPropertyName = "StringArray")] + [JsonSerializable(typeof(Dictionary<string, string>), TypeInfoPropertyName = "StringDictionary")] + public partial class CommonJsonContext : JsonSerializerContext + { + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Utilities/EmbeddedResources.cs b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs new file mode 100644 index 00000000..22b55f16 --- /dev/null +++ b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs @@ -0,0 +1,148 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Common.Utilities; +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace Ryujinx.Common +{ + public static class EmbeddedResources + { + private readonly static Assembly ResourceAssembly; + + static EmbeddedResources() + { + ResourceAssembly = Assembly.GetAssembly(typeof(EmbeddedResources)); + } + + public static byte[] Read(string filename) + { + var (assembly, path) = ResolveManifestPath(filename); + + return Read(assembly, path); + } + + public static Task<byte[]> ReadAsync(string filename) + { + var (assembly, path) = ResolveManifestPath(filename); + + return ReadAsync(assembly, path); + } + + public static byte[] Read(Assembly assembly, string filename) + { + using (var stream = GetStream(assembly, filename)) + { + if (stream == null) + { + return null; + } + + return StreamUtils.StreamToBytes(stream); + } + } + + public async static Task<byte[]> ReadAsync(Assembly assembly, string filename) + { + using (var stream = GetStream(assembly, filename)) + { + if (stream == null) + { + return null; + } + + return await StreamUtils.StreamToBytesAsync(stream); + } + } + + public static string ReadAllText(string filename) + { + var (assembly, path) = ResolveManifestPath(filename); + + return ReadAllText(assembly, path); + } + + public static Task<string> ReadAllTextAsync(string filename) + { + var (assembly, path) = ResolveManifestPath(filename); + + return ReadAllTextAsync(assembly, path); + } + + public static string ReadAllText(Assembly assembly, string filename) + { + using (var stream = GetStream(assembly, filename)) + { + if (stream == null) + { + return null; + } + + using (var reader = new StreamReader(stream)) + { + return reader.ReadToEnd(); + } + } + } + + public async static Task<string> ReadAllTextAsync(Assembly assembly, string filename) + { + using (var stream = GetStream(assembly, filename)) + { + if (stream == null) + { + return null; + } + + using (var reader = new StreamReader(stream)) + { + return await reader.ReadToEndAsync(); + } + } + } + + public static Stream GetStream(string filename) + { + var (assembly, path) = ResolveManifestPath(filename); + + return GetStream(assembly, path); + } + + public static Stream GetStream(Assembly assembly, string filename) + { + var namespace_ = assembly.GetName().Name; + var manifestUri = namespace_ + "." + filename.Replace('/', '.'); + + var stream = assembly.GetManifestResourceStream(manifestUri); + + return stream; + } + + public static string[] GetAllAvailableResources(string path, string ext = "") + { + return ResolveManifestPath(path).Item1.GetManifestResourceNames() + .Where(r => r.EndsWith(ext)) + .ToArray(); + } + + private static (Assembly, string) ResolveManifestPath(string filename) + { + var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries); + + if (segments.Length >= 2) + { + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + if (assembly.GetName().Name == segments[0]) + { + return (assembly, segments[1]); + } + } + } + + return (ResourceAssembly, filename); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Utilities/HexUtils.cs b/src/Ryujinx.Common/Utilities/HexUtils.cs new file mode 100644 index 00000000..63587cea --- /dev/null +++ b/src/Ryujinx.Common/Utilities/HexUtils.cs @@ -0,0 +1,90 @@ +using System; +using System.Text; + +namespace Ryujinx.Common +{ + public static class HexUtils + { + private static readonly char[] HexChars = "0123456789ABCDEF".ToCharArray(); + + private const int HexTableColumnWidth = 8; + private const int HexTableColumnSpace = 3; + + // Modified for Ryujinx + // Original by Pascal Ganaye - CPOL License + // https://www.codeproject.com/Articles/36747/Quick-and-Dirty-HexDump-of-a-Byte-Array + public static string HexTable(byte[] bytes, int bytesPerLine = 16) + { + if (bytes == null) + { + return "<null>"; + } + + int bytesLength = bytes.Length; + + int firstHexColumn = + HexTableColumnWidth + + HexTableColumnSpace; + + int firstCharColumn = firstHexColumn + + bytesPerLine * HexTableColumnSpace + + (bytesPerLine - 1) / HexTableColumnWidth + + 2; + + int lineLength = firstCharColumn + + bytesPerLine + + Environment.NewLine.Length; + + char[] line = (new String(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray(); + + int expectedLines = (bytesLength + bytesPerLine - 1) / bytesPerLine; + + StringBuilder result = new StringBuilder(expectedLines * lineLength); + + for (int i = 0; i < bytesLength; i += bytesPerLine) + { + line[0] = HexChars[(i >> 28) & 0xF]; + line[1] = HexChars[(i >> 24) & 0xF]; + line[2] = HexChars[(i >> 20) & 0xF]; + line[3] = HexChars[(i >> 16) & 0xF]; + line[4] = HexChars[(i >> 12) & 0xF]; + line[5] = HexChars[(i >> 8) & 0xF]; + line[6] = HexChars[(i >> 4) & 0xF]; + line[7] = HexChars[(i >> 0) & 0xF]; + + int hexColumn = firstHexColumn; + int charColumn = firstCharColumn; + + for (int j = 0; j < bytesPerLine; j++) + { + if (j > 0 && (j & 7) == 0) + { + hexColumn++; + } + + if (i + j >= bytesLength) + { + line[hexColumn] = ' '; + line[hexColumn + 1] = ' '; + line[charColumn] = ' '; + } + else + { + byte b = bytes[i + j]; + + line[hexColumn] = HexChars[(b >> 4) & 0xF]; + line[hexColumn + 1] = HexChars[b & 0xF]; + line[charColumn] = (b < 32 ? '·' : (char)b); + } + + hexColumn += 3; + charColumn++; + } + + result.Append(line); + } + + return result.ToString(); + } + } +} diff --git a/src/Ryujinx.Common/Utilities/JsonHelper.cs b/src/Ryujinx.Common/Utilities/JsonHelper.cs new file mode 100644 index 00000000..9a2d6f18 --- /dev/null +++ b/src/Ryujinx.Common/Utilities/JsonHelper.cs @@ -0,0 +1,98 @@ +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; + +namespace Ryujinx.Common.Utilities +{ + public class JsonHelper + { + private static readonly JsonNamingPolicy SnakeCasePolicy = new SnakeCaseNamingPolicy(); + private const int DefaultFileWriteBufferSize = 4096; + + /// <summary> + /// Creates new serializer options with default settings. + /// </summary> + /// <remarks> + /// It is REQUIRED for you to save returned options statically or as a part of static serializer context + /// in order to avoid performance issues. You can safely modify returned options for your case before storing. + /// </remarks> + public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true) + { + JsonSerializerOptions options = new() + { + DictionaryKeyPolicy = SnakeCasePolicy, + PropertyNamingPolicy = SnakeCasePolicy, + WriteIndented = indented, + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip + }; + + return options; + } + + public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo) + { + return JsonSerializer.Serialize(value, typeInfo); + } + + public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo) + { + return JsonSerializer.Deserialize(value, typeInfo); + } + + public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo) + { + using FileStream file = File.Create(filePath, DefaultFileWriteBufferSize, FileOptions.WriteThrough); + JsonSerializer.Serialize(file, value, typeInfo); + } + + public static T DeserializeFromFile<T>(string filePath, JsonTypeInfo<T> typeInfo) + { + using FileStream file = File.OpenRead(filePath); + return JsonSerializer.Deserialize(file, typeInfo); + } + + public static void SerializeToStream<T>(Stream stream, T value, JsonTypeInfo<T> typeInfo) + { + JsonSerializer.Serialize(stream, value, typeInfo); + } + + private class SnakeCaseNamingPolicy : JsonNamingPolicy + { + public override string ConvertName(string name) + { + if (string.IsNullOrEmpty(name)) + { + return name; + } + + StringBuilder builder = new(); + + for (int i = 0; i < name.Length; i++) + { + char c = name[i]; + + if (char.IsUpper(c)) + { + if (i == 0 || char.IsUpper(name[i - 1])) + { + builder.Append(char.ToLowerInvariant(c)); + } + else + { + builder.Append('_'); + builder.Append(char.ToLowerInvariant(c)); + } + } + else + { + builder.Append(c); + } + } + + return builder.ToString(); + } + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs b/src/Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs new file mode 100644 index 00000000..3714bec0 --- /dev/null +++ b/src/Ryujinx.Common/Utilities/MessagePackObjectFormatter.cs @@ -0,0 +1,302 @@ +using MsgPack; +using System; +using System.Text; + +namespace Ryujinx.Common.Utilities +{ + public static class MessagePackObjectFormatter + { + public static string ToString(this MessagePackObject obj, bool pretty) + { + if (pretty) + { + return Format(obj); + } + else + { + return obj.ToString(); + } + } + + public static string Format(MessagePackObject obj) + { + var builder = new IndentedStringBuilder(); + + FormatMsgPackObj(obj, builder); + + return builder.ToString(); + } + + private static void FormatMsgPackObj(MessagePackObject obj, IndentedStringBuilder builder) + { + if (obj.IsMap || obj.IsDictionary) + { + FormatMsgPackMap(obj, builder); + } + else if (obj.IsArray || obj.IsList) + { + FormatMsgPackArray(obj, builder); + } + else if (obj.IsNil) + { + builder.Append("null"); + } + else + { + var literal = obj.ToObject(); + + if (literal is String) + { + builder.AppendQuotedString(obj.AsStringUtf16()); + } + else if (literal is byte[] byteArray) + { + FormatByteArray(byteArray, builder); + } + else if (literal is MessagePackExtendedTypeObject extObject) + { + builder.Append('{'); + + // Indent + builder.IncreaseIndent() + .AppendLine(); + + // Print TypeCode field + builder.AppendQuotedString("TypeCode") + .Append(": ") + .Append(extObject.TypeCode) + .AppendLine(","); + + // Print Value field + builder.AppendQuotedString("Value") + .Append(": "); + + FormatByteArrayAsString(extObject.GetBody(), builder, true); + + // Unindent + builder.DecreaseIndent() + .AppendLine(); + + builder.Append('}'); + } + else + { + builder.Append(literal); + } + } + } + + private static void FormatByteArray(byte[] arr, IndentedStringBuilder builder) + { + builder.Append("[ "); + + foreach (var b in arr) + { + builder.Append("0x"); + builder.Append(ToHexChar(b >> 4)); + builder.Append(ToHexChar(b & 0xF)); + builder.Append(", "); + } + + // Remove trailing comma + builder.Remove(builder.Length - 2, 2); + + builder.Append(" ]"); + } + + private static void FormatByteArrayAsString(byte[] arr, IndentedStringBuilder builder, bool withPrefix) + { + builder.Append('"'); + + if (withPrefix) + { + builder.Append("0x"); + } + + foreach (var b in arr) + { + builder.Append(ToHexChar(b >> 4)); + builder.Append(ToHexChar(b & 0xF)); + } + + builder.Append('"'); + } + + private static void FormatMsgPackMap(MessagePackObject obj, IndentedStringBuilder builder) + { + var map = obj.AsDictionary(); + + builder.Append('{'); + + // Indent + builder.IncreaseIndent() + .AppendLine(); + + foreach (var item in map) + { + FormatMsgPackObj(item.Key, builder); + + builder.Append(": "); + + FormatMsgPackObj(item.Value, builder); + + builder.AppendLine(","); + } + + // Remove the trailing new line and comma + builder.TrimLastLine() + .Remove(builder.Length - 1, 1); + + // Unindent + builder.DecreaseIndent() + .AppendLine(); + + builder.Append('}'); + } + + private static void FormatMsgPackArray(MessagePackObject obj, IndentedStringBuilder builder) + { + var arr = obj.AsList(); + + builder.Append("[ "); + + foreach (var item in arr) + { + FormatMsgPackObj(item, builder); + + builder.Append(", "); + } + + // Remove trailing comma + builder.Remove(builder.Length - 2, 2); + + builder.Append(" ]"); + } + + private static char ToHexChar(int b) + { + if (b < 10) + { + return unchecked((char)('0' + b)); + } + else + { + return unchecked((char)('A' + (b - 10))); + } + } + + internal class IndentedStringBuilder + { + const string DefaultIndent = " "; + + private int _indentCount = 0; + private int _newLineIndex = 0; + private StringBuilder _builder; + + public string IndentString { get; set; } = DefaultIndent; + + public IndentedStringBuilder(StringBuilder builder) + { + _builder = builder; + } + + public IndentedStringBuilder() + : this(new StringBuilder()) + { } + + public IndentedStringBuilder(string str) + : this(new StringBuilder(str)) + { } + + public IndentedStringBuilder(int length) + : this(new StringBuilder(length)) + { } + + public int Length { get => _builder.Length; } + + public IndentedStringBuilder IncreaseIndent() + { + _indentCount++; + + return this; + } + + public IndentedStringBuilder DecreaseIndent() + { + _indentCount--; + + return this; + } + + public IndentedStringBuilder Append(char value) + { + _builder.Append(value); + + return this; + } + + public IndentedStringBuilder Append(string value) + { + _builder.Append(value); + + return this; + } + + public IndentedStringBuilder Append(object value) + { + this.Append(value.ToString()); + + return this; + } + + public IndentedStringBuilder AppendQuotedString(string value) + { + _builder.Append('"'); + _builder.Append(value); + _builder.Append('"'); + + return this; + } + + public IndentedStringBuilder AppendLine() + { + _newLineIndex = _builder.Length; + + _builder.AppendLine(); + + for (int i = 0; i < _indentCount; i++) + _builder.Append(IndentString); + + return this; + } + + public IndentedStringBuilder AppendLine(string value) + { + _builder.Append(value); + + this.AppendLine(); + + return this; + } + + public IndentedStringBuilder TrimLastLine() + { + _builder.Remove(_newLineIndex, _builder.Length - _newLineIndex); + + return this; + } + + public IndentedStringBuilder Remove(int startIndex, int length) + { + _builder.Remove(startIndex, length); + + return this; + } + + public override string ToString() + { + return _builder.ToString(); + } + } + } +} diff --git a/src/Ryujinx.Common/Utilities/NetworkHelpers.cs b/src/Ryujinx.Common/Utilities/NetworkHelpers.cs new file mode 100644 index 00000000..b2f6f45d --- /dev/null +++ b/src/Ryujinx.Common/Utilities/NetworkHelpers.cs @@ -0,0 +1,66 @@ +using System.Net.NetworkInformation; + +namespace Ryujinx.Common.Utilities +{ + public static class NetworkHelpers + { + private static (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(NetworkInterface adapter, bool isPreferred) + { + IPInterfaceProperties properties = adapter.GetIPProperties(); + + if (isPreferred || (properties.GatewayAddresses.Count > 0 && properties.DnsAddresses.Count > 0)) + { + foreach (UnicastIPAddressInformation info in properties.UnicastAddresses) + { + // Only accept an IPv4 address + if (info.Address.GetAddressBytes().Length == 4) + { + return (properties, info); + } + } + } + + return (null, null); + } + + public static (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(string lanInterfaceId = "0") + { + if (!NetworkInterface.GetIsNetworkAvailable()) + { + return (null, null); + } + + IPInterfaceProperties targetProperties = null; + UnicastIPAddressInformation targetAddressInfo = null; + + NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces(); + + string guid = lanInterfaceId; + bool hasPreference = guid != "0"; + + foreach (NetworkInterface adapter in interfaces) + { + bool isPreferred = adapter.Id == guid; + + // Ignore loopback and non IPv4 capable interface. + if (isPreferred || (targetProperties == null && adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4))) + { + (IPInterfaceProperties properties, UnicastIPAddressInformation info) = GetLocalInterface(adapter, isPreferred); + + if (properties != null) + { + targetProperties = properties; + targetAddressInfo = info; + + if (isPreferred || !hasPreference) + { + break; + } + } + } + } + + return (targetProperties, targetAddressInfo); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Utilities/SpanHelpers.cs b/src/Ryujinx.Common/Utilities/SpanHelpers.cs new file mode 100644 index 00000000..4765eab3 --- /dev/null +++ b/src/Ryujinx.Common/Utilities/SpanHelpers.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.Utilities +{ + public static class SpanHelpers + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span<T> CreateSpan<T>(scoped ref T reference, int length) + { + return MemoryMarshal.CreateSpan(ref reference, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span<T> AsSpan<T>(scoped ref T reference) where T : unmanaged + { + return CreateSpan(ref reference, 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span<TSpan> AsSpan<TStruct, TSpan>(scoped ref TStruct reference) + where TStruct : unmanaged where TSpan : unmanaged + { + return CreateSpan(ref Unsafe.As<TStruct, TSpan>(ref reference), + Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span<byte> AsByteSpan<T>(scoped ref T reference) where T : unmanaged + { + return CreateSpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan<T> CreateReadOnlySpan<T>(scoped ref T reference, int length) + { + return MemoryMarshal.CreateReadOnlySpan(ref reference, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan<T> AsReadOnlySpan<T>(scoped ref T reference) where T : unmanaged + { + return CreateReadOnlySpan(ref reference, 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(scoped ref TStruct reference) + where TStruct : unmanaged where TSpan : unmanaged + { + return CreateReadOnlySpan(ref Unsafe.As<TStruct, TSpan>(ref reference), + Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(scoped ref T reference) where T : unmanaged + { + return CreateReadOnlySpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>()); + } + } +} diff --git a/src/Ryujinx.Common/Utilities/StreamUtils.cs b/src/Ryujinx.Common/Utilities/StreamUtils.cs new file mode 100644 index 00000000..da97188d --- /dev/null +++ b/src/Ryujinx.Common/Utilities/StreamUtils.cs @@ -0,0 +1,31 @@ +using Microsoft.IO; +using Ryujinx.Common.Memory; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Ryujinx.Common.Utilities +{ + public static class StreamUtils + { + public static byte[] StreamToBytes(Stream input) + { + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) + { + input.CopyTo(stream); + + return stream.ToArray(); + } + } + + public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default) + { + using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) + { + await input.CopyToAsync(stream, cancellationToken); + + return stream.ToArray(); + } + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs b/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs new file mode 100644 index 00000000..c0127dc4 --- /dev/null +++ b/src/Ryujinx.Common/Utilities/TypedStringEnumConverter.cs @@ -0,0 +1,34 @@ +#nullable enable +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Utilities +{ + /// <summary> + /// Specifies that value of <see cref="TEnum"/> will be serialized as string in JSONs + /// </summary> + /// <remarks> + /// Trimming friendly alternative to <see cref="JsonStringEnumConverter"/>. + /// Get rid of this converter if dotnet supports similar functionality out of the box. + /// </remarks> + /// <typeparam name="TEnum">Type of enum to serialize</typeparam> + public sealed class TypedStringEnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum + { + public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var enumValue = reader.GetString(); + if (string.IsNullOrEmpty(enumValue)) + { + return default; + } + + return Enum.Parse<TEnum>(enumValue); + } + + public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } + } +} diff --git a/src/Ryujinx.Common/Utilities/UInt128Utils.cs b/src/Ryujinx.Common/Utilities/UInt128Utils.cs new file mode 100644 index 00000000..af8521b4 --- /dev/null +++ b/src/Ryujinx.Common/Utilities/UInt128Utils.cs @@ -0,0 +1,18 @@ +using System; +using System.Globalization; + +namespace Ryujinx.Common.Utilities +{ + public static class UInt128Utils + { + public static UInt128 FromHex(string hex) + { + return new UInt128(ulong.Parse(hex.AsSpan(0, 16), NumberStyles.HexNumber), ulong.Parse(hex.AsSpan(16), NumberStyles.HexNumber)); + } + + public static UInt128 CreateRandom() + { + return new UInt128((ulong)Random.Shared.NextInt64(), (ulong)Random.Shared.NextInt64()); + } + } +}
\ No newline at end of file |
