| using System; |
| using System.Collections.Generic; |
| using System.Runtime.InteropServices; |
| using System.Text; |
|
|
| namespace UnityEngine.InputSystem.Utilities |
| { |
| internal static class StringHelpers |
| { |
| |
| |
| |
| |
| public static string Escape(this string str, string chars = "\n\t\r\\\"", string replacements = "ntr\\\"") |
| { |
| if (str == null) |
| return null; |
|
|
| |
| |
| var hasCharacterThatNeedsEscaping = false; |
| foreach (var ch in str) |
| { |
| if (chars.Contains(ch)) |
| { |
| hasCharacterThatNeedsEscaping = true; |
| break; |
| } |
| } |
| if (!hasCharacterThatNeedsEscaping) |
| return str; |
|
|
| var builder = new StringBuilder(); |
| foreach (var ch in str) |
| { |
| var index = chars.IndexOf(ch); |
| if (index == -1) |
| { |
| builder.Append(ch); |
| } |
| else |
| { |
| builder.Append('\\'); |
| builder.Append(replacements[index]); |
| } |
| } |
| return builder.ToString(); |
| } |
|
|
| public static string Unescape(this string str, string chars = "ntr\\\"", string replacements = "\n\t\r\\\"") |
| { |
| if (str == null) |
| return str; |
|
|
| |
| if (!str.Contains('\\')) |
| return str; |
|
|
| var builder = new StringBuilder(); |
| for (var i = 0; i < str.Length; ++i) |
| { |
| var ch = str[i]; |
| if (ch == '\\' && i < str.Length - 2) |
| { |
| ++i; |
| ch = str[i]; |
| var index = chars.IndexOf(ch); |
| if (index != -1) |
| builder.Append(replacements[index]); |
| else |
| builder.Append(ch); |
| } |
| else |
| { |
| builder.Append(ch); |
| } |
| } |
| return builder.ToString(); |
| } |
|
|
| public static bool Contains(this string str, char ch) |
| { |
| if (str == null) |
| return false; |
| return str.IndexOf(ch) != -1; |
| } |
|
|
| public static bool Contains(this string str, string text, StringComparison comparison) |
| { |
| if (str == null) |
| return false; |
| return str.IndexOf(text, comparison) != -1; |
| } |
|
|
| public static string GetPlural(this string str) |
| { |
| if (str == null) |
| throw new ArgumentNullException(nameof(str)); |
|
|
| switch (str) |
| { |
| case "Mouse": return "Mice"; |
| case "mouse": return "mice"; |
| case "Axis": return "Axes"; |
| case "axis": return "axes"; |
| } |
|
|
| return str + 's'; |
| } |
|
|
| public static string NicifyMemorySize(long numBytes) |
| { |
| |
| if (numBytes > 1024 * 1024 * 1024) |
| { |
| var gb = numBytes / (1024 * 1024 * 1024); |
| var remainder = (numBytes % (1024 * 1024 * 1024)) / 1.0f; |
|
|
| return $"{gb + remainder} GB"; |
| } |
|
|
| |
| if (numBytes > 1024 * 1024) |
| { |
| var mb = numBytes / (1024 * 1024); |
| var remainder = (numBytes % (1024 * 1024)) / 1.0f; |
|
|
| return $"{mb + remainder} MB"; |
| } |
|
|
| |
| if (numBytes > 1024) |
| { |
| var kb = numBytes / 1024; |
| var remainder = (numBytes % 1024) / 1.0f; |
|
|
| return $"{kb + remainder} KB"; |
| } |
|
|
| |
| return $"{numBytes} Bytes"; |
| } |
|
|
| public static bool FromNicifiedMemorySize(string text, out long result, long defaultMultiplier = 1) |
| { |
| text = text.Trim(); |
|
|
| var multiplier = defaultMultiplier; |
| if (text.EndsWith("MB", StringComparison.InvariantCultureIgnoreCase)) |
| { |
| multiplier = 1024 * 1024; |
| text = text.Substring(0, text.Length - 2); |
| } |
| else if (text.EndsWith("GB", StringComparison.InvariantCultureIgnoreCase)) |
| { |
| multiplier = 1024 * 1024 * 1024; |
| text = text.Substring(0, text.Length - 2); |
| } |
| else if (text.EndsWith("KB", StringComparison.InvariantCultureIgnoreCase)) |
| { |
| multiplier = 1024; |
| text = text.Substring(0, text.Length - 2); |
| } |
| else if (text.EndsWith("Bytes", StringComparison.InvariantCultureIgnoreCase)) |
| { |
| multiplier = 1; |
| text = text.Substring(0, text.Length - "Bytes".Length); |
| } |
|
|
| if (!long.TryParse(text, out var num)) |
| { |
| result = default; |
| return false; |
| } |
|
|
| result = num * multiplier; |
| return true; |
| } |
|
|
| public static int CountOccurrences(this string str, char ch) |
| { |
| if (str == null) |
| return 0; |
|
|
| var length = str.Length; |
| var index = 0; |
| var count = 0; |
|
|
| while (index < length) |
| { |
| var nextIndex = str.IndexOf(ch, index); |
| if (nextIndex == -1) |
| break; |
|
|
| ++count; |
| index = nextIndex + 1; |
| } |
|
|
| return count; |
| } |
|
|
| public static IEnumerable<Substring> Tokenize(this string str) |
| { |
| var pos = 0; |
| var length = str.Length; |
|
|
| while (pos < length) |
| { |
| while (pos < length && char.IsWhiteSpace(str[pos])) |
| ++pos; |
|
|
| if (pos == length) |
| break; |
|
|
| if (str[pos] == '"') |
| { |
| ++pos; |
| var endPos = pos; |
| while (endPos < length && str[endPos] != '\"') |
| { |
| |
| if (str[endPos] == '\\' && endPos < length - 1) |
| ++endPos; |
| ++endPos; |
| } |
| yield return new Substring(str, pos, endPos - pos); |
| pos = endPos + 1; |
| } |
| else |
| { |
| var endPos = pos; |
| while (endPos < length && !char.IsWhiteSpace(str[endPos])) |
| ++endPos; |
| yield return new Substring(str, pos, endPos - pos); |
| pos = endPos; |
| } |
| } |
| } |
|
|
| public static IEnumerable<string> Split(this string str, Func<char, bool> predicate) |
| { |
| if (string.IsNullOrEmpty(str)) |
| yield break; |
|
|
| var length = str.Length; |
| var position = 0; |
|
|
| while (position < length) |
| { |
| |
| var ch = str[position]; |
| if (predicate(ch)) |
| { |
| ++position; |
| continue; |
| } |
|
|
| |
| var startPosition = position; |
| ++position; |
| while (position < length) |
| { |
| ch = str[position]; |
| if (predicate(ch)) |
| break; |
| ++position; |
| } |
| var endPosition = position; |
|
|
| yield return str.Substring(startPosition, endPosition - startPosition); |
| } |
| } |
|
|
| public static string Join<TValue>(string separator, params TValue[] values) |
| { |
| return Join(values, separator); |
| } |
|
|
| public static string Join<TValue>(IEnumerable<TValue> values, string separator) |
| { |
| |
| |
| var firstValue = default(string); |
| var valueCount = 0; |
| StringBuilder result = null; |
|
|
| foreach (var value in values) |
| { |
| if (value == null) |
| continue; |
| var str = value.ToString(); |
| if (string.IsNullOrEmpty(str)) |
| continue; |
|
|
| ++valueCount; |
| if (valueCount == 1) |
| { |
| firstValue = str; |
| continue; |
| } |
|
|
| if (valueCount == 2) |
| { |
| result = new StringBuilder(); |
| result.Append(firstValue); |
| } |
|
|
| result.Append(separator); |
| result.Append(str); |
| } |
|
|
| if (valueCount == 0) |
| return null; |
| if (valueCount == 1) |
| return firstValue; |
|
|
| return result.ToString(); |
| } |
|
|
| public static string MakeUniqueName<TExisting>(string baseName, IEnumerable<TExisting> existingSet, |
| Func<TExisting, string> getNameFunc) |
| { |
| if (getNameFunc == null) |
| throw new ArgumentNullException(nameof(getNameFunc)); |
|
|
| if (existingSet == null) |
| return baseName; |
|
|
| var name = baseName; |
| var nameLowerCase = name.ToLower(); |
| var nameIsUnique = false; |
| var namesTried = 1; |
|
|
| |
| if (baseName.Length > 0) |
| { |
| var lastDigit = baseName.Length; |
| while (lastDigit > 0 && char.IsDigit(baseName[lastDigit - 1])) |
| --lastDigit; |
| if (lastDigit != baseName.Length) |
| { |
| namesTried = int.Parse(baseName.Substring(lastDigit)) + 1; |
| baseName = baseName.Substring(0, lastDigit); |
| } |
| } |
|
|
| |
| while (!nameIsUnique) |
| { |
| nameIsUnique = true; |
| foreach (var existing in existingSet) |
| { |
| var existingName = getNameFunc(existing); |
| if (existingName.ToLower() == nameLowerCase) |
| { |
| name = $"{baseName}{namesTried}"; |
| nameLowerCase = name.ToLower(); |
| nameIsUnique = false; |
| ++namesTried; |
| break; |
| } |
| } |
| } |
|
|
| return name; |
| } |
|
|
| |
| public static bool CharacterSeparatedListsHaveAtLeastOneCommonElement(string firstList, string secondList, |
| char separator) |
| { |
| if (firstList == null) |
| throw new ArgumentNullException(nameof(firstList)); |
| if (secondList == null) |
| throw new ArgumentNullException(nameof(secondList)); |
|
|
| |
| |
| var indexInFirst = 0; |
| var lengthOfFirst = firstList.Length; |
| var lengthOfSecond = secondList.Length; |
| while (indexInFirst < lengthOfFirst) |
| { |
| |
| if (firstList[indexInFirst] == separator) |
| ++indexInFirst; |
|
|
| |
| var endIndexInFirst = indexInFirst + 1; |
| while (endIndexInFirst < lengthOfFirst && firstList[endIndexInFirst] != separator) |
| ++endIndexInFirst; |
| var lengthOfCurrentInFirst = endIndexInFirst - indexInFirst; |
|
|
| |
| |
| var indexInSecond = 0; |
| while (indexInSecond < lengthOfSecond) |
| { |
| |
| if (secondList[indexInSecond] == separator) |
| ++indexInSecond; |
|
|
| |
| var endIndexInSecond = indexInSecond + 1; |
| while (endIndexInSecond < lengthOfSecond && secondList[endIndexInSecond] != separator) |
| ++endIndexInSecond; |
| var lengthOfCurrentInSecond = endIndexInSecond - indexInSecond; |
|
|
| |
| if (lengthOfCurrentInFirst == lengthOfCurrentInSecond) |
| { |
| var startIndexInFirst = indexInFirst; |
| var startIndexInSecond = indexInSecond; |
|
|
| var isMatch = true; |
| for (var i = 0; i < lengthOfCurrentInFirst; ++i) |
| { |
| var first = firstList[startIndexInFirst + i]; |
| var second = secondList[startIndexInSecond + i]; |
|
|
| if (char.ToLowerInvariant(first) != char.ToLowerInvariant(second)) |
| { |
| isMatch = false; |
| break; |
| } |
| } |
|
|
| if (isMatch) |
| return true; |
| } |
|
|
| |
| indexInSecond = endIndexInSecond + 1; |
| } |
|
|
| |
| indexInFirst = endIndexInFirst + 1; |
| } |
|
|
| return false; |
| } |
|
|
| |
| |
| |
| public static int ParseInt(string str, int pos) |
| { |
| var multiply = 1; |
| var result = 0; |
| var length = str.Length; |
|
|
| while (pos < length) |
| { |
| var ch = str[pos]; |
| var digit = ch - '0'; |
| if (digit < 0 || digit > 9) |
| break; |
|
|
| result = result * multiply + digit; |
|
|
| multiply *= 10; |
| ++pos; |
| } |
|
|
| return result; |
| } |
|
|
| |
|
|
| public static bool WriteStringToBuffer(string text, IntPtr buffer, int bufferSizeInCharacters) |
| { |
| uint offset = 0; |
| return WriteStringToBuffer(text, buffer, bufferSizeInCharacters, ref offset); |
| } |
|
|
| public static unsafe bool WriteStringToBuffer(string text, IntPtr buffer, int bufferSizeInCharacters, ref uint offset) |
| { |
| if (buffer == IntPtr.Zero) |
| throw new ArgumentNullException("buffer"); |
|
|
| var length = string.IsNullOrEmpty(text) ? 0 : text.Length; |
| if (length > ushort.MaxValue) |
| throw new ArgumentException(string.Format("String exceeds max size of {0} characters", ushort.MaxValue), "text"); |
|
|
| var endOffset = offset + sizeof(char) * length + sizeof(int); |
| if (endOffset > bufferSizeInCharacters) |
| return false; |
|
|
| var ptr = ((byte*)buffer) + offset; |
| *((ushort*)ptr) = (ushort)length; |
| ptr += sizeof(ushort); |
|
|
| for (var i = 0; i < length; ++i, ptr += sizeof(char)) |
| *((char*)ptr) = text[i]; |
|
|
| offset = (uint)endOffset; |
| return true; |
| } |
|
|
| public static string ReadStringFromBuffer(IntPtr buffer, int bufferSize) |
| { |
| uint offset = 0; |
| return ReadStringFromBuffer(buffer, bufferSize, ref offset); |
| } |
|
|
| public static unsafe string ReadStringFromBuffer(IntPtr buffer, int bufferSize, ref uint offset) |
| { |
| if (buffer == IntPtr.Zero) |
| throw new ArgumentNullException(nameof(buffer)); |
|
|
| if (offset + sizeof(int) > bufferSize) |
| return null; |
|
|
| var ptr = ((byte*)buffer) + offset; |
| var length = *((ushort*)ptr); |
| ptr += sizeof(ushort); |
|
|
| if (length == 0) |
| return null; |
|
|
| var endOffset = offset + sizeof(char) * length + sizeof(int); |
| if (endOffset > bufferSize) |
| return null; |
|
|
| var text = Marshal.PtrToStringUni(new IntPtr(ptr), length); |
|
|
| offset = (uint)endOffset; |
| return text; |
| } |
|
|
| public static bool IsPrintable(this char ch) |
| { |
| |
| return !char.IsControl(ch) && !char.IsWhiteSpace(ch); |
| } |
|
|
| public static string WithAllWhitespaceStripped(this string str) |
| { |
| var buffer = new StringBuilder(); |
| foreach (var ch in str) |
| if (!char.IsWhiteSpace(ch)) |
| buffer.Append(ch); |
| return buffer.ToString(); |
| } |
|
|
| public static bool InvariantEqualsIgnoreCase(this string left, string right) |
| { |
| if (string.IsNullOrEmpty(left)) |
| return string.IsNullOrEmpty(right); |
| return string.Equals(left, right, StringComparison.InvariantCultureIgnoreCase); |
| } |
|
|
| public static string ExpandTemplateString(string template, Func<string, string> mapFunc) |
| { |
| if (string.IsNullOrEmpty(template)) |
| throw new ArgumentNullException(nameof(template)); |
| if (mapFunc == null) |
| throw new ArgumentNullException(nameof(mapFunc)); |
|
|
| var buffer = new StringBuilder(); |
|
|
| var length = template.Length; |
| for (var i = 0; i < length; ++i) |
| { |
| var ch = template[i]; |
| if (ch != '{') |
| { |
| buffer.Append(ch); |
| continue; |
| } |
|
|
| ++i; |
| var tokenStartPos = i; |
| while (i < length && template[i] != '}') |
| ++i; |
| var token = template.Substring(tokenStartPos, i - tokenStartPos); |
| |
|
|
| var mapped = mapFunc(token); |
| buffer.Append(mapped); |
| } |
|
|
| return buffer.ToString(); |
| } |
| } |
| } |
|
|