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;
        }
    }
}