using System; using System.Diagnostics; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; namespace Unity.Collections { /// /// Provides extension methods for string, UnsafeText, and NativeText. /// [GenerateTestsForBurstCompatibility] public unsafe static partial class FixedStringMethods { [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] internal static void CheckSubstringInRange(int strLength, int startIndex, int length) { if (startIndex < 0) { throw new ArgumentOutOfRangeException($"startIndex {startIndex} must be positive."); } if (length < 0) { throw new ArgumentOutOfRangeException($"length {length} cannot be negative."); } if (startIndex > strLength) { throw new ArgumentOutOfRangeException($"startIndex {startIndex} cannot be larger than string length {strLength}."); } } /// /// Retrieves a substring of this string. The substring starts from a specific character index, and has a specified length. /// /// A string type. /// A string to get the substring from. /// Start index of substring. /// Length of substring. /// A new string with length equivalent to `length` that begins at `startIndex`. /// Thrown if startIndex or length parameter is negative, or if startIndex is larger than the string length. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static T Substring(ref this T str, int startIndex, int length) where T : unmanaged, INativeList, IUTF8Bytes { CheckSubstringInRange(str.Length, startIndex, length); length = math.min(length, str.Length - startIndex); var substr = new T(); substr.Append(str.GetUnsafePtr() + startIndex, length); return substr; } /// /// Retrieves a substring of this string. The substring starts from a specific character index and continues to the end of the string. /// /// A string type. /// A string to get the substring from. /// Start index of substring. /// A new string that begins at `startIndex`. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static T Substring(ref this T str, int startIndex) where T : unmanaged, INativeList, IUTF8Bytes { return str.Substring(startIndex, str.Length - startIndex); } /// /// Retrieves a substring from this string. The substring starts from a specific character index, and has a specified length. Allocates memory to the new substring with the allocator specified. /// /// A string to get the substring from. /// Start index of substring. /// Length of substring. /// The allocator type to use. /// A `NativeText` string with a length equivalent to `length` that starts at `startIndex` and an allocator type of `allocator`. /// Thrown if startIndex or length parameter is negative, or if startIndex is larger than string length. public static NativeText Substring(ref this NativeText str, int startIndex, int length, AllocatorManager.AllocatorHandle allocator) { CheckSubstringInRange(str.Length, startIndex, length); length = math.min(length, str.Length - startIndex); var substr = new NativeText(length, allocator); substr.Append(str.GetUnsafePtr() + startIndex, length); return substr; } /// /// Retrieves a substring of this string. The substring starts from a specific character index and continues to the end of the string. Allocates memory to the new substring with the allocator specified. /// /// A string to get the substring from. /// Start index of substring. /// The allocator type to use. /// A NativeText string that begins at `startIndex` and has an allocator of type `allocator`. public static NativeText Substring(ref this NativeText str, int startIndex, AllocatorManager.AllocatorHandle allocator) { return str.Substring(startIndex, str.Length - startIndex); } /// /// Retrieves a substring of this string. The substring starts from a specific character index, and has a specified length. The new substring has the same allocator as the string. /// /// A string to get the substring from. /// Start index of substring. /// Length of substring. /// A NativeText string that has length equivalent to `length` and begins at `startIndex`. /// Thrown if startIndex or length parameter is negative, or if startIndex is larger than string length. public static NativeText Substring(ref this NativeText str, int startIndex, int length) { return str.Substring(startIndex, length, str.m_Data->m_UntypedListData.Allocator); } /// /// Retrieves a substring of this string. The substring starts from a specific character index and continues to the end of the string. The new substring has the same allocator as the string. /// /// A to get the substring from. /// Start index of substring. /// A NativeText string that begins at `startIndex`. public static NativeText Substring(ref this NativeText str, int startIndex) { return str.Substring(startIndex, str.Length - startIndex); } /// /// Returns the index of the first occurrence of a single Unicode rune in this string. /// /// A string type. /// A string to search. /// A single UTF-8 Unicode Rune to search for within this string. /// The index of the first occurrence of the byte sequence in this string. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static int IndexOf(ref this T fs, Unicode.Rune rune) where T : unmanaged, INativeList, IUTF8Bytes { var dstLen = fs.Length; int index = 0; while(index < dstLen) { int tempIndex = index; var runeAtIndex = Read(ref fs, ref tempIndex); if (runeAtIndex.value == rune.value) { return index; } index = tempIndex; } return -1; } /// /// Returns the index of the first occurrence of a byte sequence in this string. /// /// A string type. /// A string to search. /// A byte sequence to search for within this string. /// The number of bytes in the byte sequence. /// The index of the first occurrence of the byte sequence in this string. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static int IndexOf(ref this T fs, byte* bytes, int bytesLen) where T : unmanaged, INativeList, IUTF8Bytes { var dst = fs.GetUnsafePtr(); var dstLen = fs.Length; for (var i = 0; i <= dstLen - bytesLen; ++i) { for (var j = 0; j < bytesLen; ++j) if (dst[i + j] != bytes[j]) goto end_of_loop; return i; end_of_loop : {} } return -1; } /// /// Returns the index of the first occurrence of a byte sequence within a subrange of this string. /// /// A string type. /// A string to search. /// A byte sequence to search for within this string. /// The number of bytes in the byte sequence. /// The first index in this string to consider as the first byte of the byte sequence. /// The last index in this string to consider as the first byte of the byte sequence. /// The index of the first occurrence of the byte sequence in this string. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static int IndexOf(ref this T fs, byte* bytes, int bytesLen, int startIndex, int distance = Int32.MaxValue) where T : unmanaged, INativeList, IUTF8Bytes { var dst = fs.GetUnsafePtr(); var dstLen = fs.Length; var searchrange = Math.Min(distance - 1, dstLen - bytesLen); for (var i = startIndex; i <= searchrange; ++i) { for (var j = 0; j < bytesLen; ++j) if (dst[i + j] != bytes[j]) goto end_of_loop; return i; end_of_loop : {} } return -1; } /// /// Returns the index of the first occurrence of a substring within this string. /// /// A string type. /// A string type. /// A string to search. /// A substring to search for within this string. /// The index of the first occurrence of the second string within this string. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] public static int IndexOf(ref this T fs, in T2 other) where T : unmanaged, INativeList, IUTF8Bytes where T2 : unmanaged, INativeList, IUTF8Bytes { ref var oref = ref UnsafeUtilityExtensions.AsRef(in other); return fs.IndexOf(oref.GetUnsafePtr(), oref.Length); } /// /// Returns the index of the first occurrence of a substring within a subrange of this string. /// /// A string type. /// A string type. /// A string to search. /// A substring to search for within this string. /// The first index in this string to consider as an occurrence of the second string. /// The last index in this string to consider as an occurrence of the second string. /// The index of the first occurrence of the substring within this string. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] public static int IndexOf(ref this T fs, in T2 other, int startIndex, int distance = Int32.MaxValue) where T : unmanaged, INativeList, IUTF8Bytes where T2 : unmanaged, INativeList, IUTF8Bytes { ref var oref = ref UnsafeUtilityExtensions.AsRef(in other); return fs.IndexOf(oref.GetUnsafePtr(), oref.Length, startIndex, distance); } /// /// Returns true if a given substring occurs within this string. /// /// A string type. /// A string type. /// A string to search. /// A substring to search for within this string. /// True if the substring occurs within this string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] public static bool Contains(ref this T fs, in T2 other) where T : unmanaged, INativeList, IUTF8Bytes where T2 : unmanaged, INativeList, IUTF8Bytes { return fs.IndexOf(in other) != -1; } /// /// Returns the index of the last occurrence of a single Unicode rune within this string. /// /// A string type. /// A string to search. /// A single Unicode.Rune to search for within this string. /// The index of the last occurrence of the byte sequence within this string. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static int LastIndexOf(ref this T fs, Unicode.Rune rune) where T : unmanaged, INativeList, IUTF8Bytes { if (Unicode.IsValidCodePoint(rune.value)) { var dstLen = fs.Length; for (var i = dstLen - 1; i >= 0; --i) { var runeAtIndex = Peek(ref fs, i); if (Unicode.IsValidCodePoint(runeAtIndex.value) && runeAtIndex.value == rune.value) { return i; } } } return -1; } /// /// Returns the index of the last occurrence of a byte sequence within this string. /// /// A string type. /// A string to search. /// A byte sequence to search for within this string. /// The number of bytes in the byte sequence. /// The index of the last occurrence of the byte sequence within this string. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static int LastIndexOf(ref this T fs, byte* bytes, int bytesLen) where T : unmanaged, INativeList, IUTF8Bytes { var dst = fs.GetUnsafePtr(); var dstLen = fs.Length; for (var i = dstLen - bytesLen; i >= 0; --i) { for (var j = 0; j < bytesLen; ++j) if (dst[i + j] != bytes[j]) goto end_of_loop; return i; end_of_loop : {} } return -1; } /// /// Returns the index of the last occurrence of a byte sequence within a subrange of this string. /// /// A string type. /// A string to search. /// A byte sequence to search for within this string. /// The number of bytes in the byte sequence. /// The smallest index in this string to consider as the first byte of the byte sequence. /// The greatest index in this string to consider as the first byte of the byte sequence. /// The index of the last occurrence of the byte sequence within this string. Returns -1 if no occurrences found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static int LastIndexOf(ref this T fs, byte* bytes, int bytesLen, int startIndex, int distance = int.MaxValue) where T : unmanaged, INativeList, IUTF8Bytes { var dst = fs.GetUnsafePtr(); var dstLen = fs.Length; startIndex = Math.Min(dstLen - bytesLen, startIndex); var searchrange = Math.Max(0, startIndex - distance); for (var i = startIndex; i >= searchrange; --i) { for (var j = 0; j < bytesLen; ++j) if (dst[i + j] != bytes[j]) goto end_of_loop; return i; end_of_loop : {} } return -1; } /// /// Returns the index of the last occurrence of a substring within this string. /// /// A string type. /// A string type. /// A string to search. /// A substring to search for in the this string. /// The index of the last occurrence of the substring within this string. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] public static int LastIndexOf(ref this T fs, in T2 other) where T : unmanaged, INativeList, IUTF8Bytes where T2 : unmanaged, INativeList, IUTF8Bytes { ref var oref = ref UnsafeUtilityExtensions.AsRef(in other); return fs.LastIndexOf(oref.GetUnsafePtr(), oref.Length); } /// /// Returns the index of the last occurrence of a substring within a subrange of this string. /// /// A string type. /// A string type. /// A string to search. /// A substring to search for within this string. /// The greatest index in this string to consider as an occurrence of the substring. /// The smallest index in this string to consider as an occurrence of the substring. /// the index of the last occurrence of the substring within the first string. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] public static int LastIndexOf(ref this T fs, in T2 other, int startIndex, int distance = Int32.MaxValue) where T : unmanaged, INativeList, IUTF8Bytes where T2 : unmanaged, INativeList, IUTF8Bytes { ref var oref = ref UnsafeUtilityExtensions.AsRef(in other); return fs.LastIndexOf(oref.GetUnsafePtr(), oref.Length, startIndex, distance); } /// /// Returns the sort position of this string relative to a byte sequence. /// /// A string type. /// A string to compare. /// A byte sequence to compare. /// The number of bytes in the byte sequence. /// A number denoting the sort position of this string relative to the byte sequence: /// /// 0 denotes that this string and byte sequence have the same sort position.
/// -1 denotes that this string should be sorted to precede the byte sequence.
/// +1 denotes that this string should be sorted to follow the byte sequence.
///
[GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static int CompareTo(ref this T fs, byte* bytes, int bytesLen) where T : unmanaged, INativeList, IUTF8Bytes { var a = fs.GetUnsafePtr(); var aa = fs.Length; int chars = aa < bytesLen ? aa : bytesLen; for (var i = 0; i < chars; ++i) { if (a[i] < bytes[i]) return -1; if (a[i] > bytes[i]) return 1; } if (aa < bytesLen) return -1; if (aa > bytesLen) return 1; return 0; } /// /// Returns the sort position of this string relative to another. /// /// A string type. /// A string type. /// A string to compare. /// Another string to compare. /// A number denoting the relative sort position of the strings: /// /// 0 denotes that the strings have the same sort position.
/// -1 denotes that this string should be sorted to precede the other.
/// +1 denotes that this first string should be sorted to follow the other.
///
[GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] public static int CompareTo(ref this T fs, in T2 other) where T : unmanaged, INativeList, IUTF8Bytes where T2 : unmanaged, INativeList, IUTF8Bytes { ref var oref = ref UnsafeUtilityExtensions.AsRef(in other); return fs.CompareTo(oref.GetUnsafePtr(), oref.Length); } /// /// Returns true if this string and a byte sequence are equal (meaning they have the same length and content). /// /// A string type. /// A string to compare for equality. /// A sequence of bytes to compare for equality. /// The number of bytes in the byte sequence. /// True if this string and the byte sequence have the same length and if this string's character bytes match the byte sequence. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static bool Equals(ref this T fs, byte* bytes, int bytesLen) where T : unmanaged, INativeList, IUTF8Bytes { var a = fs.GetUnsafePtr(); var aa = fs.Length; if (aa != bytesLen) return false; if (a == bytes) return true; return fs.CompareTo(bytes, bytesLen) == 0; } /// /// Returns true if this string is equal to another. /// /// A string type. /// A string type. /// A string to compare for equality. /// Another string to compare for equality. /// true if the two strings have the same length and matching content. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] public static bool Equals(ref this T fs, in T2 other) where T : unmanaged, INativeList, IUTF8Bytes where T2 : unmanaged, INativeList, IUTF8Bytes { ref var oref = ref UnsafeUtilityExtensions.AsRef(in other); return fs.Equals(oref.GetUnsafePtr(), oref.Length); } /// /// Returns the Unicode.Rune at an index of this string. /// /// A string type. /// A string to read. /// A reference to an index in bytes (not characters). /// The Unicode.Rune (character) which starts at the byte index. Returns Unicode.BadRune /// if the byte(s) at the index do not form a valid UTF-8 encoded character. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static Unicode.Rune Peek(ref this T fs, int index) where T : unmanaged, INativeList, IUTF8Bytes { if (index >= fs.Length) return Unicode.BadRune; Unicode.Utf8ToUcs(out var rune, fs.GetUnsafePtr(), ref index, fs.Capacity); return rune; } /// /// Returns the Unicode.Rune at an index of this string. Increments the index to the position of the next character. /// /// A string type. /// A string to read. /// A reference to an index in bytes (not characters). Incremented by 1 to 4 depending upon the UTF-8 encoded size of the character read. /// The character (as a `Unicode.Rune`) which starts at the byte index. Returns `Unicode.BadRune` /// if the byte(s) at the index do not form a valid UTF-8 encoded character. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static Unicode.Rune Read(ref this T fs, ref int index) where T : unmanaged, INativeList, IUTF8Bytes { if (index >= fs.Length) return Unicode.BadRune; Unicode.Utf8ToUcs(out var rune, fs.GetUnsafePtr(), ref index, fs.Capacity); return rune; } /// /// Writes a Unicode.Rune at an index of this string. Increments the index to the position of the next character. /// /// A string type. /// A string to modify. /// A reference to an index in bytes (not characters). Incremented by 1 to 4 depending upon the UTF-8 encoded size of the character written. /// A rune to write to the string, encoded as UTF-8. /// FormatError.None if successful. Returns FormatError.Overflow if the index is invalid or if there is not enough space to store the encoded rune. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static FormatError Write(ref this T fs, ref int index, Unicode.Rune rune) where T : unmanaged, INativeList, IUTF8Bytes { var err = Unicode.UcsToUtf8(fs.GetUnsafePtr(), ref index, fs.Capacity, rune); if (err != ConversionError.None) return FormatError.Overflow; return FormatError.None; } /// /// Returns a copy of this string as a managed string. /// /// A string type. /// A string to copy. /// A copy of this string as a managed string. [ExcludeFromBurstCompatTesting("Returns managed string")] public static String ConvertToString(ref this T fs) where T : unmanaged, INativeList, IUTF8Bytes { var c = stackalloc char[fs.Length * 2]; int length = 0; Unicode.Utf8ToUtf16(fs.GetUnsafePtr(), fs.Length, c, out length, fs.Length * 2); return new String(c, 0, length); } /// /// Returns a hash code of this string. /// /// A string type. /// A string to get a hash code of. /// A hash code of this string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static int ComputeHashCode(ref this T fs) where T : unmanaged, INativeList, IUTF8Bytes { return (int)CollectionHelper.Hash(fs.GetUnsafePtr(), fs.Length); } /// /// Returns the effective size in bytes of this string. /// /// /// "Effective size" is `Length + 3`, the number of bytes you need to copy when serializing the string. /// (The plus 3 accounts for the null-terminator byte and the 2 bytes that store the Length). /// /// Useful for checking whether this string will fit in the space of a smaller string. /// /// A string type. /// A string to get the effective size of. /// The effective size in bytes of this string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static int EffectiveSizeOf(ref this T fs) where T : unmanaged, INativeList, IUTF8Bytes { return sizeof(ushort) + fs.Length + 1; } /// /// Returns true if a given character occurs at the beginning of this string. /// /// A string type. /// A string to search. /// A character to search for within this string. /// True if the character occurs at the beginning of this string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static bool StartsWith(ref this T fs, Unicode.Rune rune) where T : unmanaged, INativeList, IUTF8Bytes { var len = rune.LengthInUtf8Bytes(); return fs.Length >= len && 0 == UTF8ArrayUnsafeUtility.StrCmp(fs.GetUnsafePtr(), len, &rune, 1) ; } /// /// Returns true if a given substring occurs at the beginning of this string. /// /// A string type. /// A string type. /// A string to search. /// A substring to search for within this string. /// True if the substring occurs at the beginning of this string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] public static bool StartsWith(ref this T fs, in U other) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes { var len = other.Length; return fs.Length >= len && 0 == UTF8ArrayUnsafeUtility.StrCmp(fs.GetUnsafePtr(), len, other.GetUnsafePtr(), len) ; } /// /// Returns true if a given character occurs at the end of this string. /// /// A string type. /// A string to search. /// A character to search for within this string. /// True if the character occurs at the end of this string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static bool EndsWith(ref this T fs, Unicode.Rune rune) where T : unmanaged, INativeList, IUTF8Bytes { var len = rune.LengthInUtf8Bytes(); return fs.Length >= len && 0 == UTF8ArrayUnsafeUtility.StrCmp(fs.GetUnsafePtr() + fs.Length - len, len, &rune, 1) ; } /// /// Returns true if a given substring occurs at the end of this string. /// /// A string type. /// A string type. /// A string to search. /// A substring to search for within this string. /// True if the substring occurs at the end of this string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes), typeof(FixedString128Bytes) })] public static bool EndsWith(ref this T fs, in U other) where T : unmanaged, INativeList, IUTF8Bytes where U : unmanaged, INativeList, IUTF8Bytes { var len = other.Length; return fs.Length >= len && 0 == UTF8ArrayUnsafeUtility.StrCmp(fs.GetUnsafePtr() + fs.Length - len, len, other.GetUnsafePtr(), len) ; } [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] internal static int TrimStartIndex(ref this T fs) where T : unmanaged, INativeList, IUTF8Bytes { var lengthInBytes = fs.Length; var ptr = fs.GetUnsafePtr(); int index = 0; while (true) { var prev = index; var error = Unicode.Utf8ToUcs(out var rune, ptr, ref index, lengthInBytes); if (error != ConversionError.None || !rune.IsWhiteSpace()) { index -= index - prev; break; } } return index; } [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] internal static int TrimStartIndex(ref this T fs, ReadOnlySpan trimRunes) where T : unmanaged, INativeList, IUTF8Bytes { var lengthInBytes = fs.Length; var ptr = fs.GetUnsafePtr(); int index = 0; while (true) { var prev = index; var error = Unicode.Utf8ToUcs(out var rune, ptr, ref index, lengthInBytes); var doTrim = false; for (int i = 0, num = trimRunes.Length; i < num && !doTrim; i++) { doTrim |= trimRunes[i] == rune; } if (error != ConversionError.None || !doTrim) { index -= index - prev; break; } } return index; } [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] internal static int TrimEndIndex(ref this T fs) where T : unmanaged, INativeList, IUTF8Bytes { var lengthInBytes = fs.Length; var ptr = fs.GetUnsafePtr(); int index = lengthInBytes; while (true) { var prev = index; var error = Unicode.Utf8ToUcsReverse(out var rune, ptr, ref index, lengthInBytes); if (error != ConversionError.None || !rune.IsWhiteSpace()) { index += prev - index; break; } } return index; } [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] internal static int TrimEndIndex(ref this T fs, ReadOnlySpan trimRunes) where T : unmanaged, INativeList, IUTF8Bytes { var lengthInBytes = fs.Length; var ptr = fs.GetUnsafePtr(); int index = lengthInBytes; while (true) { var prev = index; var error = Unicode.Utf8ToUcsReverse(out var rune, ptr, ref index, lengthInBytes); var doTrim = false; for (int i = 0, num = trimRunes.Length; i < num && !doTrim; i++) { doTrim |= trimRunes[i] == rune; } if (error != ConversionError.None || !doTrim) { index += prev - index; break; } } return index; } /// /// Removes whitespace characters from begining of the string. /// /// A string type. /// A string to perform operation. /// Returns instance of this string with whitespace characters removed from the start of the string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static T TrimStart(ref this T fs) where T : unmanaged, INativeList, IUTF8Bytes { var index = fs.TrimStartIndex(); var result = new T(); result.Append(fs.GetUnsafePtr() + index, fs.Length - index); return result; } /// /// Removes whitespace characters from begining of the string. /// /// A string to perform operation. /// The allocator type to use. /// Returns instance of this string with whitespace characters removed from the start of the string. public static UnsafeText TrimStart(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator) { var index = fs.TrimStartIndex(); var lengthInBytes = fs.Length - index; var result = new UnsafeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr() + index, lengthInBytes); return result; } /// /// Removes whitespace characters from begining of the string. /// /// A string to perform operation. /// The allocator type to use. /// Returns instance of this string with whitespace characters removed from the start of the string. public static NativeText TrimStart(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator) { var index = fs.TrimStartIndex(); var lengthInBytes = fs.Length - index; var result = new NativeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr() + index, lengthInBytes); return result; } /// /// Removes specific characters from begining of the string. /// /// A string type. /// A string to perform operation. /// Runes that should be trimmed. /// Returns instance of this string with specific characters removed from the start of the string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static T TrimStart(ref this T fs, ReadOnlySpan trimRunes) where T : unmanaged, INativeList, IUTF8Bytes { var index = fs.TrimStartIndex(trimRunes); var result = new T(); result.Append(fs.GetUnsafePtr() + index, fs.Length - index); return result; } /// /// Removes specific characters characters from begining of the string. /// /// A string to perform operation. /// The allocator type to use. /// Runes that should be trimmed. /// Returns instance of this string with specific characters removed from the start of the string. public static UnsafeText TrimStart(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan trimRunes) { var index = fs.TrimStartIndex(trimRunes); var lengthInBytes = fs.Length - index; var result = new UnsafeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr() + index, lengthInBytes); return result; } /// /// Removes specific characters from begining of the string. /// /// A string to perform operation. /// The allocator type to use. /// Runes that should be trimmed. /// Returns instance of this string with specific characters removed from the start of the string. public static NativeText TrimStart(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan trimRunes) { var index = fs.TrimStartIndex(trimRunes); var lengthInBytes = fs.Length - index; var result = new NativeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr() + index, lengthInBytes); return result; } /// /// Removes whitespace characters from the end of the string. /// /// A string type. /// A string to perform operation. /// Returns instance of this string with whitespace characters removed from the end of the string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static T TrimEnd(ref this T fs) where T : unmanaged, INativeList, IUTF8Bytes { var index = fs.TrimEndIndex(); var result = new T(); result.Append(fs.GetUnsafePtr(), index); return result; } /// /// Removes whitespace characters from the end of the string. /// /// A string to perform operation. /// The allocator type to use. /// Returns instance of this string with whitespace characters removed from the end of the string. public static UnsafeText TrimEnd(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator) { var index = fs.TrimEndIndex(); var lengthInBytes = index; var result = new UnsafeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr(), lengthInBytes); return result; } /// /// Removes whitespace characters from the end of the string. /// /// A string to perform operation. /// The allocator type to use. /// Returns instance of this string with whitespace characters removed from the end of the string. public static NativeText TrimEnd(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator) { var index = fs.TrimEndIndex(); var lengthInBytes = index; var result = new NativeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr(), lengthInBytes); return result; } /// /// Removes specific characters from the end of the string. /// /// A string type. /// A string to perform operation. /// Runes that should be trimmed. /// Returns instance of this string with specific characters removed from the end of the string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static T TrimEnd(ref this T fs, ReadOnlySpan trimRunes) where T : unmanaged, INativeList, IUTF8Bytes { var index = fs.TrimEndIndex(trimRunes); var result = new T(); result.Append(fs.GetUnsafePtr(), index); return result; } /// /// Removes specific characters from the end of the string. /// /// A string to perform operation. /// The allocator type to use. /// Runes that should be trimmed. /// Returns instance of this string with specific characters removed from the end of the string. public static UnsafeText TrimEnd(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan trimRunes) { var index = fs.TrimEndIndex(trimRunes); var lengthInBytes = index; var result = new UnsafeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr(), lengthInBytes); return result; } /// /// Removes specific characters from the end of the string. /// /// A string to perform operation. /// The allocator type to use. /// Runes that should be trimmed. /// Returns instance of this string with specific characters removed from the end of the string. public static NativeText TrimEnd(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan trimRunes) { var index = fs.TrimEndIndex(trimRunes); var lengthInBytes = index; var result = new NativeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr(), lengthInBytes); return result; } /// /// Removes whitespace characters from the begining and the end of the string. /// /// A string type. /// A string to perform operation. /// Returns instance of this string with whitespace characters removed from the begining and the end of the string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static T Trim(ref this T fs) where T : unmanaged, INativeList, IUTF8Bytes { var start = fs.TrimStartIndex(); if (start == fs.Length) { return new T(); } var end = fs.TrimEndIndex(); var result = new T(); result.Append(fs.GetUnsafePtr() + start, end - start); return result; } /// /// Removes whitespace characters from the begining and the end of the string. /// /// A string to perform operation. /// The allocator type to use. /// Returns instance of this string with whitespace characters removed from the begining and the end of the string. public static UnsafeText Trim(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator) { var start = fs.TrimStartIndex(); if (start == fs.Length) { return new UnsafeText(0, allocator); } var end = fs.TrimEndIndex(); var lengthInBytes = end - start; var result = new UnsafeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr() + start, lengthInBytes); return result; } /// /// Removes whitespace characters from the begining and the end of the string. /// /// A string to perform operation. /// The allocator type to use. /// Returns instance of this string with whitespace characters removed from the begining and the end of the string. public static NativeText Trim(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator) { var start = fs.TrimStartIndex(); if (start == fs.Length) { return new NativeText(0, allocator); } var end = fs.TrimEndIndex(); var lengthInBytes = end - start; var result = new NativeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr() + start, lengthInBytes); return result; } /// /// Removes specific characters from the begining and the end of the string. /// /// A string type. /// A string to perform operation. /// Runes that should be trimmed. /// Returns instance of this string with specific characters removed from the begining and the end of the string. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static T Trim(ref this T fs, ReadOnlySpan trimRunes) where T : unmanaged, INativeList, IUTF8Bytes { var start = fs.TrimStartIndex(trimRunes); if (start == fs.Length) { return new T(); } var end = fs.TrimEndIndex(trimRunes); var result = new T(); result.Append(fs.GetUnsafePtr() + start, end - start); return result; } /// /// Removes specific characters from the begining and the end of the string. /// /// A string to perform operation. /// The allocator type to use. /// Runes that should be trimmed. /// Returns instance of this string with specific characters removed from the begining and the end of the string. public static UnsafeText Trim(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan trimRunes) { var start = fs.TrimStartIndex(trimRunes); if (start == fs.Length) { return new UnsafeText(0, allocator); } var end = fs.TrimEndIndex(); var lengthInBytes = end - start; var result = new UnsafeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr() + start, lengthInBytes); return result; } /// /// Removes specific characters from the begining and the end of the string. /// /// A string to perform operation. /// The allocator type to use. /// Runes that should be trimmed. /// Returns instance of this string with specific characters removed from the begining and the end of the string. public static NativeText Trim(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator, ReadOnlySpan trimRunes) { var start = fs.TrimStartIndex(trimRunes); if (start == fs.Length) { return new NativeText(0, allocator); } var end = fs.TrimEndIndex(); var lengthInBytes = end - start; var result = new NativeText(lengthInBytes, allocator); result.Append(fs.GetUnsafePtr() + start, lengthInBytes); return result; } /// /// Converts string to lowercase only ASCII characters. /// /// A string type. /// A string to perform operation. /// Returns a copy of this string converted to lowercase ASCII. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static T ToLowerAscii(ref this T fs) where T : unmanaged, INativeList, IUTF8Bytes { var lengthInBytes = fs.Length; var ptr = fs.GetUnsafePtr(); T result = new T(); Unicode.Rune rune; var error = ConversionError.None; for (var i = 0; i < lengthInBytes && error == ConversionError.None;) { error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes); result.Append(rune.ToLowerAscii()); } return result; } /// /// Converts string to lowercase only ASCII characters. /// /// A string to perform operation. /// The allocator type to use. /// Returns a copy of this string converted to lowercase ASCII. public static UnsafeText ToLowerAscii(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator) { var lengthInBytes = fs.Length; var ptr = fs.GetUnsafePtr(); var result = new UnsafeText(lengthInBytes, allocator); Unicode.Rune rune; var error = ConversionError.None; for (var i = 0; i < lengthInBytes && error == ConversionError.None;) { error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes); result.Append(rune.ToLowerAscii()); } return result; } /// /// Converts string to lowercase only ASCII characters. /// /// A string to perform operation. /// The allocator type to use. /// Returns a copy of this string converted to lowercase ASCII. public static NativeText ToLowerAscii(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator) { var lengthInBytes = fs.Length; var ptr = fs.GetUnsafePtr(); var result = new NativeText(lengthInBytes, allocator); Unicode.Rune rune; var error = ConversionError.None; for (var i = 0; i < lengthInBytes && error == ConversionError.None;) { error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes); result.Append(rune.ToLowerAscii()); } return result; } /// /// Converts string to uppercase only ASCII characters. /// /// A string type. /// A string to perform operation. /// Returns a copy of this string converted to uppercase ASCII. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedString128Bytes) })] public static T ToUpperAscii(ref this T fs) where T : unmanaged, INativeList, IUTF8Bytes { var lengthInBytes = fs.Length; var ptr = fs.GetUnsafePtr(); T result = new T(); Unicode.Rune rune; var error = ConversionError.None; for (var i = 0; i < lengthInBytes && error == ConversionError.None;) { error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes); result.Append(rune.ToUpperAscii()); } return result; } /// /// Converts string to uppercase only ASCII characters. /// /// A string to perform operation. /// The allocator type to use. /// Returns a copy of this string converted to uppercase ASCII. public static UnsafeText ToUpperAscii(ref this UnsafeText fs, AllocatorManager.AllocatorHandle allocator) { var lengthInBytes = fs.Length; var ptr = fs.GetUnsafePtr(); var result = new UnsafeText(lengthInBytes, allocator); Unicode.Rune rune; var error = ConversionError.None; for (var i = 0; i < lengthInBytes && error == ConversionError.None;) { error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes); result.Append(rune.ToUpperAscii()); } return result; } /// /// Converts string to uppercase only ASCII characters. /// /// A string to perform operation. /// The allocator type to use. /// Returns a copy of this string converted to uppercase ASCII. public static NativeText ToUpperAscii(ref this NativeText fs, AllocatorManager.AllocatorHandle allocator) { var lengthInBytes = fs.Length; var ptr = fs.GetUnsafePtr(); var result = new NativeText(lengthInBytes, allocator); Unicode.Rune rune; var error = ConversionError.None; for (var i = 0; i < lengthInBytes && error == ConversionError.None;) { error = Unicode.Utf8ToUcs(out rune, ptr, ref i, lengthInBytes); result.Append(rune.ToUpperAscii()); } return result; } } }