using System; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace PKHeX.Core; public static partial class Util { /// /// 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 (char.IsAsciiDigit(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 (!char.IsAsciiDigit(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 (char.IsAsciiDigit(c)) { result <<= 4; result += (uint)(c - '0'); } else if (char.IsAsciiHexDigitUpper(c)) { result <<= 4; result += (uint)(c - 'A' + 10); } else if (char.IsAsciiHexDigitLower(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 (char.IsAsciiDigit(c)) { result <<= 4; result += (uint)(c - '0'); } else if (char.IsAsciiHexDigitUpper(c)) { result <<= 4; result += (uint)(c - 'A' + 10); } else if (char.IsAsciiHexDigitLower(c)) { result <<= 4; result += (uint)(c - 'a' + 10); } } return result; } /// /// Parses a variable length hex string (non-spaced, bytes in reverse order). /// public static byte[] GetBytesFromHexString(ReadOnlySpan input) { byte[] result = new byte[input.Length / 2]; GetBytesFromHexString(input, result); return result; } /// public static void GetBytesFromHexString(ReadOnlySpan input, Span result) { for (int i = 0; i < result.Length; i++) { var slice = input.Slice(i * 2, 2); result[^(i + 1)] = (byte)GetHexValue(slice); } } private const string HexChars = "0123456789ABCDEF"; public static string GetHexStringFromBytes(ReadOnlySpan data) { System.Diagnostics.Debug.Assert(data.Length is (4 or 8 or 12 or 16)); Span result = stackalloc char[data.Length * 2]; for (int i = 0; i < data.Length; i++) { // Write tuples from the opposite side of the result buffer. var offset = (data.Length - i - 1) * 2; result[offset + 0] = HexChars[data[i] >> 4]; result[offset + 1] = HexChars[data[i] & 0xF]; } return new string(result); } /// /// Filters the string down to only valid hex characters, returning a new string. /// /// Input string to filter public static string GetOnlyHex(ReadOnlySpan str) { if (str.IsWhiteSpace()) return string.Empty; int ctr = 0; Span result = stackalloc char[str.Length]; foreach (var c in str) { if (char.IsAsciiHexDigit(c)) result[ctr++] = c; } return new string(result[..ctr]); } /// /// Returns a new string with each word converted to its appropriate title case. /// /// Input string to modify public static string ToTitleCase(ReadOnlySpan span) { if (span.IsEmpty) return string.Empty; Span result = stackalloc char[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; for (var i = 0; i < span.Length; i++) { char c = span[i]; if (char.IsWhiteSpace(c)) { first = true; } else if (first) { c = char.ToUpper(c); first = false; } else { c = char.ToLower(c); } result[i] = c; } return new string(result); } /// /// 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]; } public static Dictionary[] GetMultiDictionary(IReadOnlyList> nameArray, int start) { var result = new Dictionary[nameArray.Count]; for (int i = 0; i < result.Length; i++) result[i] = GetDictionary(nameArray[i], start); return result; } private static Dictionary GetDictionary(IReadOnlyList names, int start) { var result = new Dictionary(names.Count - start); for (int i = start; i < names.Count; i++) result.Add(names[i], i); return result; } }