359 lines
14 KiB
C#
359 lines
14 KiB
C#
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<TBufferWriter>(ref TBufferWriter sb, ReadOnlySpan<char> chars)
|
|
where TBufferWriter : System.Buffers.IBufferWriter<byte>
|
|
{
|
|
var span = sb.GetSpan(UTF8NoBom.GetMaxByteCount(chars.Length));
|
|
sb.Advance(UTF8NoBom.GetBytes(chars, span));
|
|
}
|
|
|
|
/// <summary>Create the Utf16 string StringBuilder.</summary>
|
|
public static Utf16ValueStringBuilder CreateStringBuilder()
|
|
{
|
|
return new Utf16ValueStringBuilder(false);
|
|
}
|
|
|
|
/// <summary>Create the Utf16 string StringBuilder, when true uses thread-static buffer that is faster but must return immediately.</summary>
|
|
public static Utf8ValueStringBuilder CreateUtf8StringBuilder()
|
|
{
|
|
return new Utf8ValueStringBuilder(false);
|
|
}
|
|
|
|
/// <summary>Create the Utf8(`Span[byte]`) StringBuilder.</summary>
|
|
/// <param name="notNested">
|
|
/// If true uses thread-static buffer that is faster but must return immediately.
|
|
/// </param>
|
|
/// <exception cref="InvalidOperationException">
|
|
/// This exception is thrown when <c>new StringBuilder(disposeImmediately: true)</c> or <c>ZString.CreateUtf8StringBuilder(notNested: true)</c> is nested.
|
|
/// See the README.md
|
|
/// </exception>
|
|
public static Utf16ValueStringBuilder CreateStringBuilder(bool notNested)
|
|
{
|
|
return new Utf16ValueStringBuilder(notNested);
|
|
}
|
|
|
|
/// <summary>Create the Utf8(`Span[byte]`) StringBuilder, when true uses thread-static buffer that is faster but must return immediately.</summary>
|
|
/// <param name="notNested">
|
|
/// If true uses thread-static buffer that is faster but must return immediately.
|
|
/// </param>
|
|
/// <exception cref="InvalidOperationException">
|
|
/// This exception is thrown when <c>new StringBuilder(disposeImmediately: true)</c> or <c>ZString.CreateUtf8StringBuilder(notNested: true)</c> is nested.
|
|
/// See the README.md
|
|
/// </exception>
|
|
public static Utf8ValueStringBuilder CreateUtf8StringBuilder(bool notNested)
|
|
{
|
|
return new Utf8ValueStringBuilder(notNested);
|
|
}
|
|
|
|
/// <summary>Concatenates the elements of an array, using the specified separator between each element.</summary>
|
|
public static string Join<T>(char separator, params T[] values)
|
|
{
|
|
ReadOnlySpan<char> s = stackalloc char[1] { separator };
|
|
return JoinInternal<T>(s, values.AsSpan());
|
|
}
|
|
|
|
/// <summary>Concatenates the elements of an array, using the specified separator between each element.</summary>
|
|
public static string Join<T>(char separator, List<T> values)
|
|
{
|
|
ReadOnlySpan<char> s = stackalloc char[1] { separator };
|
|
return JoinInternal(s, (IReadOnlyList<T>)values);
|
|
}
|
|
|
|
/// <summary>Concatenates the elements of an array, using the specified separator between each element.</summary>
|
|
public static string Join<T>(char separator, ReadOnlySpan<T> values)
|
|
{
|
|
ReadOnlySpan<char> s = stackalloc char[1] { separator };
|
|
return JoinInternal(s, values);
|
|
}
|
|
|
|
/// <summary>Concatenates the elements of an array, using the specified separator between each element.</summary>
|
|
public static string Join<T>(char separator, IEnumerable<T> values)
|
|
{
|
|
ReadOnlySpan<char> s = stackalloc char[1] { separator };
|
|
return JoinInternal(s, values);
|
|
}
|
|
|
|
public static string Join<T>(char separator, ICollection<T> values)
|
|
{
|
|
ReadOnlySpan<char> s = stackalloc char[1] { separator };
|
|
return JoinInternal(s, values.AsEnumerable());
|
|
}
|
|
|
|
public static string Join<T>(char separator, IList<T> values)
|
|
{
|
|
ReadOnlySpan<char> s = stackalloc char[1] { separator };
|
|
return JoinInternal(s, values);
|
|
}
|
|
|
|
public static string Join<T>(char separator, IReadOnlyList<T> values)
|
|
{
|
|
ReadOnlySpan<char> s = stackalloc char[1] { separator };
|
|
return JoinInternal(s, values);
|
|
}
|
|
|
|
public static string Join<T>(char separator, IReadOnlyCollection<T> values)
|
|
{
|
|
ReadOnlySpan<char> s = stackalloc char[1] { separator };
|
|
return JoinInternal(s, values.AsEnumerable());
|
|
}
|
|
|
|
/// <summary>Concatenates the elements of an array, using the specified separator between each element.</summary>
|
|
public static string Join<T>(string separator, params T[] values)
|
|
{
|
|
return JoinInternal<T>(separator.AsSpan(), values.AsSpan());
|
|
}
|
|
|
|
/// <summary>Concatenates the elements of an array, using the specified separator between each element.</summary>
|
|
public static string Join<T>(string separator, List<T> values)
|
|
{
|
|
return JoinInternal(separator.AsSpan(), (IReadOnlyList<T>)values);
|
|
}
|
|
|
|
/// <summary>Concatenates the elements of an array, using the specified separator between each element.</summary>
|
|
public static string Join<T>(string separator, ReadOnlySpan<T> values)
|
|
{
|
|
return JoinInternal(separator.AsSpan(), values);
|
|
}
|
|
|
|
public static string Join<T>(string separator, ICollection<T> values)
|
|
{
|
|
return JoinInternal(separator.AsSpan(), values.AsEnumerable());
|
|
}
|
|
|
|
public static string Join<T>(string separator, IList<T> values)
|
|
{
|
|
return JoinInternal(separator.AsSpan(), values);
|
|
}
|
|
|
|
public static string Join<T>(string separator, IReadOnlyList<T> values)
|
|
{
|
|
return JoinInternal(separator.AsSpan(), values);
|
|
}
|
|
|
|
public static string Join<T>(string separator, IReadOnlyCollection<T> values)
|
|
{
|
|
return JoinInternal(separator.AsSpan(), values.AsEnumerable());
|
|
}
|
|
|
|
/// <summary>Concatenates the elements of an array, using the specified separator between each element.</summary>
|
|
public static string Join<T>(string separator, IEnumerable<T> values)
|
|
{
|
|
return JoinInternal(separator.AsSpan(), values);
|
|
}
|
|
|
|
#if NETSTANDARD2_1_OR_GREATER || NET_STANDARD_2_1
|
|
/// <summary>Concatenates the elements of an array, using the specified separator between each element.</summary>
|
|
public static string Join(char separator, ReadOnlySpan<string> values)
|
|
{
|
|
ReadOnlySpan<char> s = stackalloc char[1] { separator };
|
|
return JoinInternal(s, values);
|
|
}
|
|
|
|
/// <summary>Concatenates the elements of an array, using the specified separator between each element.</summary>
|
|
public static string Join(char separator, params string[] values)
|
|
{
|
|
ReadOnlySpan<char> s = stackalloc char[1] { separator };
|
|
return JoinInternal(s, (ReadOnlySpan<string>)values.AsSpan());
|
|
}
|
|
|
|
/// <summary>Concatenates the elements of an array, using the specified separator between each element.</summary>
|
|
public static string Join(string separator, params string[] values)
|
|
{
|
|
return JoinInternal(separator.AsSpan(), (ReadOnlySpan<string>)values.AsSpan());
|
|
}
|
|
#endif
|
|
|
|
/// <summary>Concatenates the string representation of some specified objects.</summary>
|
|
public static string Concat<T>(params T[] values)
|
|
{
|
|
return JoinInternal<T>(default, values.AsSpan());
|
|
}
|
|
|
|
/// <summary>Concatenates the string representation of some specified objects.</summary>
|
|
public static string Concat<T>(List<T> values)
|
|
{
|
|
return JoinInternal(default, (IReadOnlyList<T>)values);
|
|
}
|
|
|
|
/// <summary>Concatenates the string representation of some specified objects.</summary>
|
|
public static string Concat<T>(ReadOnlySpan<T> values)
|
|
{
|
|
return JoinInternal(default, values);
|
|
}
|
|
|
|
/// <summary>Concatenates the string representation of some specified objects.</summary>
|
|
public static string Concat<T>(ICollection<T> values)
|
|
{
|
|
return JoinInternal(default, values.AsEnumerable());
|
|
}
|
|
|
|
/// <summary>Concatenates the string representation of some specified objects.</summary>
|
|
public static string Concat<T>(IList<T> values)
|
|
{
|
|
return JoinInternal(default, values);
|
|
}
|
|
|
|
/// <summary>Concatenates the string representation of some specified objects.</summary>
|
|
public static string Concat<T>(IReadOnlyList<T> values)
|
|
{
|
|
return JoinInternal(default, values);
|
|
}
|
|
|
|
/// <summary>Concatenates the string representation of some specified objects.</summary>
|
|
public static string Concat<T>(IReadOnlyCollection<T> values)
|
|
{
|
|
return JoinInternal(default, values.AsEnumerable());
|
|
}
|
|
|
|
/// <summary>Concatenates the string representation of some specified objects.</summary>
|
|
public static string Concat<T>(IEnumerable<T> values)
|
|
{
|
|
return JoinInternal(default, values);
|
|
}
|
|
|
|
static string JoinInternal<T>(ReadOnlySpan<char> separator, IList<T> values)
|
|
{
|
|
var readOnlyList = values as IReadOnlyList<T>;
|
|
// Boxing will occur, but JIT will be de-virtualized.
|
|
readOnlyList = readOnlyList ?? new ReadOnlyListAdaptor<T>(values);
|
|
return JoinInternal(separator, readOnlyList);
|
|
}
|
|
|
|
static string JoinInternal<T>(ReadOnlySpan<char> separator, IReadOnlyList<T> 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<string> 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<T>(ReadOnlySpan<char> separator, ReadOnlySpan<T> values)
|
|
{
|
|
if (values.Length == 0)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
else if (typeof(T) == typeof(string) && values.Length == 1)
|
|
{
|
|
return Unsafe.As<string>(values[0]);
|
|
}
|
|
|
|
var sb = new Utf16ValueStringBuilder(true);
|
|
try
|
|
{
|
|
sb.AppendJoinInternal(separator, values);
|
|
return sb.ToString();
|
|
}
|
|
finally
|
|
{
|
|
sb.Dispose();
|
|
}
|
|
}
|
|
|
|
static string JoinInternal<T>(ReadOnlySpan<char> separator, IEnumerable<T> 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<char> separator, ReadOnlySpan<string> 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
|
|
}
|
|
}
|