using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; namespace Cysharp.Text { public static partial class ZString { static Encoding UTF8NoBom = new UTF8Encoding(false); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void AppendChars(ref TBufferWriter sb, ReadOnlySpan chars) where TBufferWriter : System.Buffers.IBufferWriter { var span = sb.GetSpan(UTF8NoBom.GetMaxByteCount(chars.Length)); sb.Advance(UTF8NoBom.GetBytes(chars, span)); } /// Create the Utf16 string StringBuilder. public static Utf16ValueStringBuilder CreateStringBuilder() { return new Utf16ValueStringBuilder(false); } /// Create the Utf16 string StringBuilder, when true uses thread-static buffer that is faster but must return immediately. public static Utf8ValueStringBuilder CreateUtf8StringBuilder() { return new Utf8ValueStringBuilder(false); } /// Create the Utf8(`Span[byte]`) StringBuilder. /// /// If true uses thread-static buffer that is faster but must return immediately. /// /// /// This exception is thrown when new StringBuilder(disposeImmediately: true) or ZString.CreateUtf8StringBuilder(notNested: true) is nested. /// See the README.md /// public static Utf16ValueStringBuilder CreateStringBuilder(bool notNested) { return new Utf16ValueStringBuilder(notNested); } /// Create the Utf8(`Span[byte]`) StringBuilder, when true uses thread-static buffer that is faster but must return immediately. /// /// If true uses thread-static buffer that is faster but must return immediately. /// /// /// This exception is thrown when new StringBuilder(disposeImmediately: true) or ZString.CreateUtf8StringBuilder(notNested: true) is nested. /// See the README.md /// public static Utf8ValueStringBuilder CreateUtf8StringBuilder(bool notNested) { return new Utf8ValueStringBuilder(notNested); } /// Concatenates the elements of an array, using the specified separator between each element. public static string Join(char separator, params T[] values) { ReadOnlySpan s = stackalloc char[1] { separator }; return JoinInternal(s, values.AsSpan()); } /// Concatenates the elements of an array, using the specified separator between each element. public static string Join(char separator, List values) { ReadOnlySpan s = stackalloc char[1] { separator }; return JoinInternal(s, (IReadOnlyList)values); } /// Concatenates the elements of an array, using the specified separator between each element. public static string Join(char separator, ReadOnlySpan values) { ReadOnlySpan s = stackalloc char[1] { separator }; return JoinInternal(s, values); } /// Concatenates the elements of an array, using the specified separator between each element. public static string Join(char separator, IEnumerable values) { ReadOnlySpan s = stackalloc char[1] { separator }; return JoinInternal(s, values); } public static string Join(char separator, ICollection values) { ReadOnlySpan s = stackalloc char[1] { separator }; return JoinInternal(s, values.AsEnumerable()); } public static string Join(char separator, IList values) { ReadOnlySpan s = stackalloc char[1] { separator }; return JoinInternal(s, values); } public static string Join(char separator, IReadOnlyList values) { ReadOnlySpan s = stackalloc char[1] { separator }; return JoinInternal(s, values); } public static string Join(char separator, IReadOnlyCollection values) { ReadOnlySpan s = stackalloc char[1] { separator }; return JoinInternal(s, values.AsEnumerable()); } /// Concatenates the elements of an array, using the specified separator between each element. public static string Join(string separator, params T[] values) { return JoinInternal(separator.AsSpan(), values.AsSpan()); } /// Concatenates the elements of an array, using the specified separator between each element. public static string Join(string separator, List values) { return JoinInternal(separator.AsSpan(), (IReadOnlyList)values); } /// Concatenates the elements of an array, using the specified separator between each element. public static string Join(string separator, ReadOnlySpan values) { return JoinInternal(separator.AsSpan(), values); } public static string Join(string separator, ICollection values) { return JoinInternal(separator.AsSpan(), values.AsEnumerable()); } public static string Join(string separator, IList values) { return JoinInternal(separator.AsSpan(), values); } public static string Join(string separator, IReadOnlyList values) { return JoinInternal(separator.AsSpan(), values); } public static string Join(string separator, IReadOnlyCollection values) { return JoinInternal(separator.AsSpan(), values.AsEnumerable()); } /// Concatenates the elements of an array, using the specified separator between each element. public static string Join(string separator, IEnumerable values) { return JoinInternal(separator.AsSpan(), values); } #if NETSTANDARD2_1_OR_GREATER || NET_STANDARD_2_1 /// Concatenates the elements of an array, using the specified separator between each element. public static string Join(char separator, ReadOnlySpan values) { ReadOnlySpan s = stackalloc char[1] { separator }; return JoinInternal(s, values); } /// Concatenates the elements of an array, using the specified separator between each element. public static string Join(char separator, params string[] values) { ReadOnlySpan s = stackalloc char[1] { separator }; return JoinInternal(s, (ReadOnlySpan)values.AsSpan()); } /// Concatenates the elements of an array, using the specified separator between each element. public static string Join(string separator, params string[] values) { return JoinInternal(separator.AsSpan(), (ReadOnlySpan)values.AsSpan()); } #endif /// Concatenates the string representation of some specified objects. public static string Concat(params T[] values) { return JoinInternal(default, values.AsSpan()); } /// Concatenates the string representation of some specified objects. public static string Concat(List values) { return JoinInternal(default, (IReadOnlyList)values); } /// Concatenates the string representation of some specified objects. public static string Concat(ReadOnlySpan values) { return JoinInternal(default, values); } /// Concatenates the string representation of some specified objects. public static string Concat(ICollection values) { return JoinInternal(default, values.AsEnumerable()); } /// Concatenates the string representation of some specified objects. public static string Concat(IList values) { return JoinInternal(default, values); } /// Concatenates the string representation of some specified objects. public static string Concat(IReadOnlyList values) { return JoinInternal(default, values); } /// Concatenates the string representation of some specified objects. public static string Concat(IReadOnlyCollection values) { return JoinInternal(default, values.AsEnumerable()); } /// Concatenates the string representation of some specified objects. public static string Concat(IEnumerable values) { return JoinInternal(default, values); } static string JoinInternal(ReadOnlySpan separator, IList values) { var readOnlyList = values as IReadOnlyList; // Boxing will occur, but JIT will be de-virtualized. readOnlyList = readOnlyList ?? new ReadOnlyListAdaptor(values); return JoinInternal(separator, readOnlyList); } static string JoinInternal(ReadOnlySpan separator, IReadOnlyList values) { if (values.Count == 0) { return string.Empty; } #if NETSTANDARD2_1_OR_GREATER || NET_STANDARD_2_1 if (values is string[] valueArray) { return JoinInternal(separator, valueArray.AsSpan()); } #if NET5_0_OR_GREATER if (values is List valueList) { return JoinInternal(separator, CollectionsMarshal.AsSpan(valueList)); } #endif #endif var sb = new Utf16ValueStringBuilder(true); try { sb.AppendJoinInternal(separator, values); return sb.ToString(); } finally { sb.Dispose(); } } static string JoinInternal(ReadOnlySpan separator, ReadOnlySpan values) { if (values.Length == 0) { return string.Empty; } else if (typeof(T) == typeof(string) && values.Length == 1) { return Unsafe.As(values[0]); } var sb = new Utf16ValueStringBuilder(true); try { sb.AppendJoinInternal(separator, values); return sb.ToString(); } finally { sb.Dispose(); } } static string JoinInternal(ReadOnlySpan separator, IEnumerable values) { var sb = new Utf16ValueStringBuilder(true); try { sb.AppendJoinInternal(separator, values); return sb.ToString(); } finally { sb.Dispose(); } } #if NETSTANDARD2_1_OR_GREATER || NET_STANDARD_2_1 static string JoinInternal(ReadOnlySpan separator, ReadOnlySpan values) { if (values.Length == 0) { return string.Empty; } if (values.Length == 1) { return values[0]; } var totalSeparatorsLength = (values.Length - 1) * separator.Length; var totalLength = totalSeparatorsLength; for (var i = 0; i < values.Length; i++) { if (values[i] is { } value) { totalLength += value.Length; } } if (totalLength == 0) { return string.Empty; } var resultString = string.Create(totalLength, 0, (_, _) => { }); var writeBuffer = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(resultString.AsSpan()), resultString.Length); var copiedLength = 0; for (var i = 0; i < values.Length; i++) { if (values[i] is { } value) { value.AsSpan().CopyTo(writeBuffer.Slice(copiedLength)); copiedLength += value.Length; } // Fill in the separator if (i < values.Length - 1) { if (separator.Length == 1) { writeBuffer[copiedLength++] = separator[0]; } else { separator.CopyTo(writeBuffer.Slice(copiedLength)); copiedLength += separator.Length; } } } return resultString; } #endif } }