com.alicizax.unity.cysharp..../ZString.cs
陈思海 4fbea560b5 init
2025-01-09 13:57:51 +08:00

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