using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; namespace PKHeX.Core; public static partial class Util { /// public static int ToInt32(string value) => ToInt32(value.AsSpan()); /// public static uint ToUInt32(string value) => ToUInt32(value.AsSpan()); /// public static uint GetHexValue(string value) => GetHexValue(value.AsSpan()); /// public static ulong GetHexValue64(string value) => GetHexValue64(value.AsSpan()); /// public static byte[] GetBytesFromHexString(string value) => GetBytesFromHexString(value.AsSpan()); /// public static string GetHexStringFromBytes(byte[] data, int offset, int length) => GetHexStringFromBytes(data.AsSpan(offset, length)); /// /// Parses the string into an , skipping all characters except for valid digits. /// /// String to parse /// Parsed value public static int ToInt32(ReadOnlySpan value) { int result = 0; if (value.Length == 0) return result; bool negative = false; foreach (var c in value) { if (IsNum(c)) { result *= 10; result += c; result -= '0'; } else if (c == '-' && result == 0) { negative = true; } } return negative ? -result : result; } /// /// Parses the string into a , skipping all characters except for valid digits. /// /// String to parse /// Parsed value public static uint ToUInt32(ReadOnlySpan value) { uint result = 0; if (value.Length == 0) return result; foreach (var c in value) { if (!IsNum(c)) continue; result *= 10; result += (uint)(c - '0'); } return result; } /// /// Parses the hex string into a , skipping all characters except for valid digits. /// /// Hex String to parse /// Parsed value public static uint GetHexValue(ReadOnlySpan value) { uint result = 0; if (value.Length == 0) return result; foreach (var c in value) { if (IsNum(c)) { result <<= 4; result += (uint)(c - '0'); } else if (IsHexUpper(c)) { result <<= 4; result += (uint)(c - 'A' + 10); } else if (IsHexLower(c)) { result <<= 4; result += (uint)(c - 'a' + 10); } } return result; } /// /// Parses the hex string into a , skipping all characters except for valid digits. /// /// Hex String to parse /// Parsed value public static ulong GetHexValue64(ReadOnlySpan value) { ulong result = 0; if (value.Length == 0) return result; foreach (var c in value) { if (IsNum(c)) { result <<= 4; result += (uint)(c - '0'); } else if (IsHexUpper(c)) { result <<= 4; result += (uint)(c - 'A' + 10); } else if (IsHexLower(c)) { result <<= 4; result += (uint)(c - 'a' + 10); } } return result; } public static byte[] GetBytesFromHexString(ReadOnlySpan seed) { byte[] result = new byte[seed.Length / 2]; for (int i = 0; i < result.Length; i++) { var slice = seed.Slice(i * 2, 2); result[^(i+1)] = (byte)GetHexValue(slice); } return result; } public static string GetHexStringFromBytes(ReadOnlySpan arr) { var sb = new StringBuilder(arr.Length * 2); for (int i = arr.Length - 1; i >= 0; i--) sb.AppendFormat("{0:X2}", arr[i]); return sb.ToString(); } private static bool IsNum(char c) => (uint)(c - '0') <= 9; private static bool IsHexUpper(char c) => (uint)(c - 'A') <= 5; private static bool IsHexLower(char c) => (uint)(c - 'a') <= 5; private static bool IsHex(char c) => IsNum(c) || IsHexUpper(c) || IsHexLower(c); /// /// Filters the string down to only valid hex characters, returning a new string. /// /// Input string to filter public static string GetOnlyHex(string str) { if (string.IsNullOrWhiteSpace(str)) return string.Empty; var sb = new StringBuilder(str.Length); foreach (var c in str) { if (IsHex(c)) sb.Append(c); } return sb.ToString(); } /// /// Returns a new string with each word converted to its appropriate title case. /// /// Input string to modify /// Trim ends of whitespace public static string ToTitleCase(ReadOnlySpan str, bool trim = false) { int start = 0; if (trim) { // Get First index that isn't a space while (start < str.Length && char.IsWhiteSpace(str[start])) start++; } if (start == str.Length) return string.Empty; int end = str.Length - 1; if (trim) { // Get Last index that isn't a space while (end > start && char.IsWhiteSpace(str[end])) end--; } var span = str.Slice(start, end - start + 1); var sb = new StringBuilder(span.Length); // Add each word to the string builder. Continue from the first index that isn't a space. // Add the first character as uppercase, then add each successive character as lowercase. bool first = true; foreach (char c in span) { if (char.IsWhiteSpace(c)) { first = true; sb.Append(c); } else if (first) { sb.Append(char.ToUpper(c)); first = false; } else { sb.Append(char.ToLower(c)); } } return sb.ToString(); } /// /// Trims a string at the first instance of a 0x0000 terminator. /// /// String to trim. /// Trimmed string. public static string TrimFromZero(string input) => TrimFromFirst(input, '\0'); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static string TrimFromFirst(string input, char c) { int index = input.IndexOf(c); return index < 0 ? input : input[..index]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void TrimFromFirst(StringBuilder input, char c) { for (int i = 0; i < input.Length; i++) { if (input[i] != c) continue; input.Remove(i, input.Length - i); return; } } public static Dictionary[] GetMultiDictionary(IReadOnlyList> nameArray) { var result = new Dictionary[nameArray.Count]; for (int i = 0; i < result.Length; i++) { var dict = result[i] = new Dictionary(); var names = nameArray[i]; for (int j = 0; j < names.Count; j++) dict.Add(names[j], j); } return result; } }