fix(core): Optimizes strings / .NET 5 compatibility changes (#354)
- [X] Removes some string allocations (e.g. split) - [X] Optimizes some collections - [X] Converts insensitive to extension methods of built-ins. - [X] Adds ordinal (case sensitive) string helpers - [X] Fixes conditionals for in-game commands so they use Ordinal comparisons. - [X] Replaces ToLower.Contains with InsensitiveContains - [X] Adds ValueStringBuilder - [X] Implements ValueStringBuilder in a few places where it makes sense - [X] Removes the redundant Wrap function and replaces it with an optimized version - [X] Fixes list conversions in Utility Closes #351 Bumps release version
This commit is contained in:
parent
92aae8d482
commit
77ce2e1980
117 changed files with 2000 additions and 1226 deletions
363
Projects/Server/Buffers/ValueStringBuilder.cs
Normal file
363
Projects/Server/Buffers/ValueStringBuilder.cs
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Server.Buffers
|
||||
{
|
||||
public ref struct ValueStringBuilder
|
||||
{
|
||||
private char[]? _arrayToReturnToPool;
|
||||
private Span<char> _chars;
|
||||
|
||||
public ValueStringBuilder(Span<char> initialBuffer)
|
||||
{
|
||||
_arrayToReturnToPool = null;
|
||||
_chars = initialBuffer;
|
||||
Length = 0;
|
||||
}
|
||||
|
||||
public ValueStringBuilder(int initialCapacity)
|
||||
{
|
||||
_arrayToReturnToPool = ArrayPool<char>.Shared.Rent(initialCapacity);
|
||||
_chars = _arrayToReturnToPool;
|
||||
Length = 0;
|
||||
}
|
||||
|
||||
public int Length { get; set; }
|
||||
|
||||
public int Capacity => _chars.Length;
|
||||
|
||||
public void EnsureCapacity(int capacity)
|
||||
{
|
||||
if (capacity > _chars.Length)
|
||||
{
|
||||
Grow(capacity - Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a pinnable reference to the builder.
|
||||
/// Does not ensure there is a null char after <see cref="Length"/>
|
||||
/// This overload is pattern matched in the C# 7.3+ compiler so you can omit
|
||||
/// the explicit method call, and write eg "fixed (char* c = builder)"
|
||||
/// </summary>
|
||||
public ref char GetPinnableReference() => ref MemoryMarshal.GetReference(_chars);
|
||||
|
||||
/// <summary>
|
||||
/// Get a pinnable reference to the builder.
|
||||
/// </summary>
|
||||
/// <param name="terminate">Ensures that the builder has a null char after <see cref="Length"/></param>
|
||||
public ref char GetPinnableReference(bool terminate)
|
||||
{
|
||||
if (terminate)
|
||||
{
|
||||
EnsureCapacity(Length + 1);
|
||||
_chars[Length] = '\0';
|
||||
}
|
||||
return ref MemoryMarshal.GetReference(_chars);
|
||||
}
|
||||
|
||||
public ref char this[int index] => ref _chars[index];
|
||||
|
||||
public override string ToString() => _chars.Slice(0, Length).ToString();
|
||||
|
||||
/// <summary>Returns the underlying storage of the builder.</summary>
|
||||
public Span<char> RawChars => _chars;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a span around the contents of the builder.
|
||||
/// </summary>
|
||||
/// <param name="terminate">Ensures that the builder has a null char after <see cref="Length"/></param>
|
||||
public ReadOnlySpan<char> AsSpan(bool terminate)
|
||||
{
|
||||
if (terminate)
|
||||
{
|
||||
EnsureCapacity(Length + 1);
|
||||
_chars[Length] = '\0';
|
||||
}
|
||||
return _chars.Slice(0, Length);
|
||||
}
|
||||
|
||||
public ReadOnlySpan<char> AsSpan() => _chars.Slice(0, Length);
|
||||
public ReadOnlySpan<char> AsSpan(int start) => _chars.Slice(start, Length - start);
|
||||
public ReadOnlySpan<char> AsSpan(int start, int length) => _chars.Slice(start, length);
|
||||
|
||||
public bool TryCopyTo(Span<char> destination, out int charsWritten)
|
||||
{
|
||||
if (_chars.Slice(0, Length).TryCopyTo(destination))
|
||||
{
|
||||
charsWritten = Length;
|
||||
return true;
|
||||
}
|
||||
|
||||
charsWritten = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Insert(int index, char value, int count)
|
||||
{
|
||||
if (Length > _chars.Length - count)
|
||||
{
|
||||
Grow(count);
|
||||
}
|
||||
|
||||
int remaining = Length - index;
|
||||
_chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
|
||||
_chars.Slice(index, count).Fill(value);
|
||||
Length += count;
|
||||
}
|
||||
|
||||
public void Insert(int index, string? s)
|
||||
{
|
||||
if (s == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int count = s.Length;
|
||||
|
||||
if (Length > _chars.Length - count)
|
||||
{
|
||||
Grow(count);
|
||||
}
|
||||
|
||||
int remaining = Length - index;
|
||||
_chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
|
||||
s.AsSpan().CopyTo(_chars.Slice(index));
|
||||
Length += count;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Append(char c)
|
||||
{
|
||||
int pos = Length;
|
||||
if ((uint)pos < (uint)_chars.Length)
|
||||
{
|
||||
_chars[pos] = c;
|
||||
Length = pos + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
GrowAndAppend(c);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Append(string? s)
|
||||
{
|
||||
if (s == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int pos = Length;
|
||||
if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc.
|
||||
{
|
||||
_chars[pos] = s[0];
|
||||
Length = pos + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendSlow(s);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AppendLine(string? s)
|
||||
{
|
||||
if (s == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc.
|
||||
if (s.Length == 1)
|
||||
{
|
||||
Append(s[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendSlow(s);
|
||||
}
|
||||
|
||||
Append(Environment.NewLine);
|
||||
}
|
||||
|
||||
private void AppendSlow(string s)
|
||||
{
|
||||
int pos = Length;
|
||||
if (pos > _chars.Length - s.Length)
|
||||
{
|
||||
Grow(s.Length);
|
||||
}
|
||||
|
||||
s.AsSpan().CopyTo(_chars.Slice(pos));
|
||||
Length += s.Length;
|
||||
}
|
||||
|
||||
public void Append(char c, int count)
|
||||
{
|
||||
if (Length > _chars.Length - count)
|
||||
{
|
||||
Grow(count);
|
||||
}
|
||||
|
||||
Span<char> dst = _chars.Slice(Length, count);
|
||||
for (int i = 0; i < dst.Length; i++)
|
||||
{
|
||||
dst[i] = c;
|
||||
}
|
||||
Length += count;
|
||||
}
|
||||
|
||||
public unsafe void Append(char* value, int length)
|
||||
{
|
||||
int pos = Length;
|
||||
if (pos > _chars.Length - length)
|
||||
{
|
||||
Grow(length);
|
||||
}
|
||||
|
||||
Span<char> dst = _chars.Slice(Length, length);
|
||||
for (int i = 0; i < dst.Length; i++)
|
||||
{
|
||||
dst[i] = *value++;
|
||||
}
|
||||
Length += length;
|
||||
}
|
||||
|
||||
public void Append(ReadOnlySpan<char> value)
|
||||
{
|
||||
int pos = Length;
|
||||
if (pos > _chars.Length - value.Length)
|
||||
{
|
||||
Grow(value.Length);
|
||||
}
|
||||
|
||||
value.CopyTo(_chars.Slice(Length));
|
||||
Length += value.Length;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<char> AppendSpan(int length)
|
||||
{
|
||||
int origPos = Length;
|
||||
if (origPos > _chars.Length - length)
|
||||
{
|
||||
Grow(length);
|
||||
}
|
||||
|
||||
Length = origPos + length;
|
||||
return _chars.Slice(origPos, length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void GrowAndAppend(char c)
|
||||
{
|
||||
Grow(1);
|
||||
Append(c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resize the internal buffer either by doubling current buffer size or
|
||||
/// by adding <paramref name="additionalCapacityBeyondPos"/> to
|
||||
/// <see cref="Length"/> whichever is greater.
|
||||
/// </summary>
|
||||
/// <param name="additionalCapacityBeyondPos">
|
||||
/// Number of chars requested beyond current position.
|
||||
/// </param>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void Grow(int additionalCapacityBeyondPos)
|
||||
{
|
||||
char[] poolArray = ArrayPool<char>.Shared.Rent(Math.Max(Length + additionalCapacityBeyondPos, _chars.Length * 2));
|
||||
|
||||
_chars.Slice(0, Length).CopyTo(poolArray);
|
||||
|
||||
char[]? toReturn = _arrayToReturnToPool;
|
||||
_chars = _arrayToReturnToPool = poolArray;
|
||||
if (toReturn != null)
|
||||
{
|
||||
ArrayPool<char>.Shared.Return(toReturn);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose()
|
||||
{
|
||||
char[]? toReturn = _arrayToReturnToPool;
|
||||
this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
|
||||
if (toReturn != null)
|
||||
{
|
||||
ArrayPool<char>.Shared.Return(toReturn);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Replace(char oldChar, char newChar, int startIndex, int count)
|
||||
{
|
||||
int currentLength = Length;
|
||||
if ((uint)startIndex > (uint)currentLength)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex));
|
||||
}
|
||||
|
||||
if (count < 0 || startIndex > currentLength - count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
|
||||
var slice = _chars;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var indexOf = slice.IndexOf(oldChar);
|
||||
if (indexOf == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
slice[indexOf] = newChar;
|
||||
slice = slice.Slice(indexOf + 1);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Remove(int startIndex, int length)
|
||||
{
|
||||
if (length < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
}
|
||||
|
||||
if (startIndex < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex));
|
||||
}
|
||||
|
||||
if (length > Length - startIndex)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
}
|
||||
|
||||
if (startIndex == 0)
|
||||
{
|
||||
_chars = _chars.Slice(length);
|
||||
}
|
||||
else if (startIndex + length == Length)
|
||||
{
|
||||
_chars = _chars.Slice(0, startIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Somewhere in the middle, this will be slow
|
||||
_chars.Slice(startIndex + length).CopyTo(_chars.Slice(startIndex));
|
||||
}
|
||||
|
||||
Length -= length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Server.Buffers;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
|
|
@ -32,7 +33,7 @@ namespace Server
|
|||
|
||||
try
|
||||
{
|
||||
var br1 = fmt.IndexOf('.', StringComparison.Ordinal);
|
||||
var br1 = fmt.IndexOfOrdinal('.');
|
||||
var br2 = fmt.IndexOf('.', br1 + 1);
|
||||
|
||||
var br3 = br2 + 1;
|
||||
|
|
@ -60,15 +61,15 @@ namespace Server
|
|||
}
|
||||
}
|
||||
|
||||
if (fmt.Contains("god", StringComparison.Ordinal) || fmt.Contains("gq", StringComparison.Ordinal))
|
||||
if (fmt.InsensitiveContains("god") || fmt.InsensitiveContains("gq"))
|
||||
{
|
||||
Type = ClientType.God;
|
||||
}
|
||||
else if (fmt.Contains("third dawn", StringComparison.Ordinal) ||
|
||||
fmt.Contains("uo:td", StringComparison.Ordinal) ||
|
||||
fmt.Contains("uotd", StringComparison.Ordinal) ||
|
||||
fmt.Contains("uo3d", StringComparison.Ordinal) ||
|
||||
fmt.Contains("uo:3d", StringComparison.Ordinal))
|
||||
else if (fmt.InsensitiveContains("third dawn") ||
|
||||
fmt.InsensitiveContains("uo:td") ||
|
||||
fmt.InsensitiveContains("uotd") ||
|
||||
fmt.InsensitiveContains("uo3d") ||
|
||||
fmt.InsensitiveContains("uo:3d"))
|
||||
{
|
||||
Type = ClientType.UOTD;
|
||||
}
|
||||
|
|
@ -178,13 +179,13 @@ namespace Server
|
|||
|
||||
private string ToStringImpl()
|
||||
{
|
||||
var builder = new StringBuilder(16);
|
||||
using var builder = new ValueStringBuilder(stackalloc char[32]);
|
||||
|
||||
builder.Append(Major);
|
||||
builder.Append(Major.ToString());
|
||||
builder.Append('.');
|
||||
builder.Append(Minor);
|
||||
builder.Append(Minor.ToString());
|
||||
builder.Append('.');
|
||||
builder.Append(Revision);
|
||||
builder.Append(Revision.ToString());
|
||||
|
||||
if (Major <= 5 && Minor <= 0 && Revision <= 6) // Anything before 5.0.7
|
||||
{
|
||||
|
|
@ -196,7 +197,7 @@ namespace Server
|
|||
else
|
||||
{
|
||||
builder.Append('.');
|
||||
builder.Append(Patch);
|
||||
builder.Append(Patch.ToString());
|
||||
}
|
||||
|
||||
if (Type != ClientType.Regular)
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ namespace Server
|
|||
|
||||
public static bool Handle(Mobile from, string text, MessageType type = MessageType.Regular)
|
||||
{
|
||||
if (!text.StartsWith(Prefix, StringComparison.Ordinal) && type != MessageType.Command)
|
||||
if (!text.StartsWithOrdinal(Prefix) && type != MessageType.Command)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -198,7 +198,7 @@ namespace Server
|
|||
text = text.Substring(Prefix.Length);
|
||||
}
|
||||
|
||||
var indexOf = text.IndexOf(' ', StringComparison.Ordinal);
|
||||
var indexOf = text.IndexOfOrdinal(' ');
|
||||
|
||||
string command;
|
||||
string[] args;
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ namespace Server
|
|||
break;
|
||||
}
|
||||
|
||||
if (!ipStr.Contains(':', StringComparison.Ordinal))
|
||||
if (!ipStr.ContainsOrdinal(':'))
|
||||
{
|
||||
ipStr += ":2593";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace Server
|
|||
|
||||
public static Point2D Parse(string value)
|
||||
{
|
||||
var start = value.IndexOf('(', StringComparison.Ordinal);
|
||||
var start = value.IndexOfOrdinal('(');
|
||||
var end = value.IndexOf(',', start + 1);
|
||||
|
||||
Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var x);
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ namespace Server
|
|||
|
||||
public static Point3D Parse(string value)
|
||||
{
|
||||
var start = value.IndexOf('(', StringComparison.Ordinal);
|
||||
var start = value.IndexOfOrdinal('(');
|
||||
var end = value.IndexOf(',', start + 1);
|
||||
|
||||
Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var x);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace Server
|
|||
|
||||
public static Rectangle2D Parse(string value)
|
||||
{
|
||||
var start = value.IndexOf('(', StringComparison.Ordinal);
|
||||
var start = value.IndexOfOrdinal('(');
|
||||
var end = value.IndexOf(',', start + 1);
|
||||
|
||||
Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var x);
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ namespace Server
|
|||
|
||||
public static WorldLocation Parse(string value)
|
||||
{
|
||||
var start = value.IndexOf('(', StringComparison.Ordinal);
|
||||
var start = value.IndexOfOrdinal('(');
|
||||
var end = value.IndexOf(',', start + 1);
|
||||
|
||||
Utility.ToInt32(value.Substring(start + 1, end - (start + 1)).Trim(), out var x);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
|
@ -55,16 +54,26 @@ namespace Server.Guilds
|
|||
public static BaseGuild FindByAbbrev(string abbr) =>
|
||||
World.Guilds.Values.FirstOrDefault(g => g.Abbreviation == abbr);
|
||||
|
||||
public static List<BaseGuild> Search(string find)
|
||||
public static HashSet<BaseGuild> Search(string find)
|
||||
{
|
||||
var words = find.ToLower().Split(' ');
|
||||
var results = new List<BaseGuild>();
|
||||
var results = new HashSet<BaseGuild>();
|
||||
|
||||
foreach (var g in World.Guilds.Values)
|
||||
{
|
||||
var name = g.Name.ToLower();
|
||||
var name = g.Name;
|
||||
|
||||
if (words.All(t => name.IndexOf(t, StringComparison.Ordinal) != -1))
|
||||
bool all = true;
|
||||
foreach (var t in words)
|
||||
{
|
||||
if (name.InsensitiveIndexOf(t) == -1)
|
||||
{
|
||||
all = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all)
|
||||
{
|
||||
results.Add(g);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public static class Insensitive
|
||||
{
|
||||
public static IComparer<string> Comparer { get; } = StringComparer.OrdinalIgnoreCase;
|
||||
|
||||
public static int Compare(string a, string b) => Comparer.Compare(a, b);
|
||||
|
||||
public static bool Equals(string a, string b) =>
|
||||
a == null && b == null || a != null && b != null && a.Length == b.Length && Comparer.Compare(a, b) == 0;
|
||||
|
||||
public static bool StartsWith(string a, string b) =>
|
||||
a != null && b != null && a.Length >= b.Length && Comparer.Compare(a.Substring(0, b.Length), b) == 0;
|
||||
|
||||
public static bool EndsWith(string a, string b) =>
|
||||
a != null && b != null && a.Length >= b.Length && Comparer.Compare(a.Substring(a.Length - b.Length), b) == 0;
|
||||
|
||||
public static bool Contains(string a, string b) =>
|
||||
a != null && b != null && a.Length >= b.Length && a.Contains(b, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
|
@ -1823,7 +1823,7 @@ namespace Server.Items
|
|||
{
|
||||
line = line.Trim();
|
||||
|
||||
if (line.Length == 0 || line.StartsWith("#", StringComparison.Ordinal))
|
||||
if (line.Length == 0 || line.StartsWithOrdinal("#"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using Server;
|
||||
|
||||
namespace System.Text.Json.Serialization
|
||||
{
|
||||
public class NullableStructSerializerFactory : JsonConverterFactory
|
||||
|
|
@ -26,8 +28,7 @@ namespace System.Text.Json.Serialization
|
|||
|
||||
var structType = typeToConvert.GenericTypeArguments[0];
|
||||
return !structType.IsPrimitive &&
|
||||
structType.Namespace?.StartsWith(nameof(System), StringComparison.Ordinal) != true &&
|
||||
!structType.IsEnum;
|
||||
structType.Namespace.StartsWithOrdinal(nameof(System)) && !structType.IsEnum;
|
||||
}
|
||||
|
||||
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) =>
|
||||
|
|
|
|||
|
|
@ -61,9 +61,7 @@ namespace Server
|
|||
public static bool IsRunningFromXUnit =>
|
||||
m_IsRunningFromXUnit ??= AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Any(
|
||||
a => a.FullName?
|
||||
.ToUpperInvariant()
|
||||
.StartsWith("XUNIT", StringComparison.Ordinal) ?? false
|
||||
a => a.FullName.InsensitiveStartsWith("XUNIT")
|
||||
);
|
||||
|
||||
public static bool Profiling
|
||||
|
|
@ -346,7 +344,7 @@ namespace Server
|
|||
|
||||
foreach (var a in args)
|
||||
{
|
||||
if (Insensitive.Equals(a, "-profile"))
|
||||
if (a.InsensitiveEquals("-profile"))
|
||||
{
|
||||
Profiling = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -397,7 +397,7 @@ namespace Server
|
|||
return null;
|
||||
}
|
||||
|
||||
if (Insensitive.Equals(value, "Internal"))
|
||||
if (value.InsensitiveEquals("Internal"))
|
||||
{
|
||||
return Internal;
|
||||
}
|
||||
|
|
@ -419,7 +419,7 @@ namespace Server
|
|||
continue;
|
||||
}
|
||||
|
||||
if (index >= 0 && map.MapIndex == index || Insensitive.Equals(map.Name, value))
|
||||
if (index >= 0 && map.MapIndex == index || map.Name.InsensitiveEquals(value))
|
||||
{
|
||||
return map;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Server
|
|||
|
||||
while ((line = ip.ReadLine()) != null)
|
||||
{
|
||||
if (line.Length == 0 || line.StartsWith("#", StringComparison.Ordinal))
|
||||
if (line.Length == 0 || line.StartsWithOrdinal("#"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Runtime.CompilerServices;
|
|||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using Server.Accounting;
|
||||
using Server.Buffers;
|
||||
using Server.ContextMenus;
|
||||
using Server.Guilds;
|
||||
using Server.Gumps;
|
||||
|
|
@ -5674,8 +5675,7 @@ namespace Server
|
|||
return false;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder(text.Length, text.Length);
|
||||
|
||||
using var sb = new ValueStringBuilder(stackalloc char[Math.Min(text.Length, 256)]);
|
||||
for (var i = 0; i < text.Length; ++i)
|
||||
{
|
||||
sb.Append(text[i] != ' ' ? GhostChars.RandomElement() : ' ');
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ namespace Server
|
|||
|
||||
stream.Seek(offset + headerSize, SeekOrigin.Begin);
|
||||
|
||||
Span<byte> sourceData = new byte[compressedSize];
|
||||
Span<byte> sourceData = GC.AllocateUninitializedArray<byte>(compressedSize);
|
||||
|
||||
if (stream.Read(sourceData) != compressedSize)
|
||||
{
|
||||
|
|
@ -163,7 +163,7 @@ namespace Server
|
|||
|
||||
if (compressionMethod == 1)
|
||||
{
|
||||
data = new byte[decompressedSize];
|
||||
data = GC.AllocateUninitializedArray<byte>(decompressedSize);
|
||||
Zlib.Unpack(data, ref decompressedSize, sourceData, compressedSize);
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -84,9 +84,9 @@ namespace Server.Network
|
|||
HuePickers = new List<HuePicker>();
|
||||
Menus = new List<IMenu>();
|
||||
Trades = new List<SecureTrade>();
|
||||
_recvBuffer = new byte[RecvPipeSize];
|
||||
_recvBuffer = GC.AllocateUninitializedArray<byte>(RecvPipeSize);
|
||||
RecvPipe = new Pipe<byte>(_recvBuffer);
|
||||
_sendBuffer = new byte[SendPipeSize];
|
||||
_sendBuffer = GC.AllocateUninitializedArray<byte>(SendPipeSize);
|
||||
SendPipe = new Pipe<byte>(_sendBuffer);
|
||||
m_NextCheckActivity = Core.TickCount + 30000;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using Microsoft.Toolkit.HighPerformance.Extensions;
|
||||
using Server.Diagnostics;
|
||||
using Server.Gumps;
|
||||
|
||||
|
|
@ -133,7 +134,8 @@ namespace Server.Network
|
|||
}
|
||||
case 0x24: // Use skill
|
||||
{
|
||||
if (!int.TryParse(command.Split(' ')[0], out var skillIndex))
|
||||
var tokenizer = command.Tokenize(' ');
|
||||
if (!tokenizer.MoveNext() || !int.TryParse(tokenizer.Current, out var skillIndex))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
@ -155,15 +157,11 @@ namespace Server.Network
|
|||
}
|
||||
case 0x27: // Cast spell from book
|
||||
{
|
||||
var split = command.Split(' ');
|
||||
var tokenizer = command.Tokenize(' ');
|
||||
var spellID = (tokenizer.MoveNext() ? Utility.ToInt32(tokenizer.Current) : 0) - 1;
|
||||
var serial = tokenizer.MoveNext() ? Utility.ToUInt32(tokenizer.Current) : (uint)Serial.MinusOne;
|
||||
|
||||
if (split.Length > 0)
|
||||
{
|
||||
var spellID = Utility.ToInt32(split[0]) - 1;
|
||||
var serial = split.Length > 1 ? Utility.ToUInt32(split[1]) : (uint)Serial.MinusOne;
|
||||
|
||||
EventSink.InvokeCastSpellRequest(from, spellID, World.FindItem(serial));
|
||||
}
|
||||
EventSink.InvokeCastSpellRequest(from, spellID, World.FindItem(serial));
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ namespace Server
|
|||
|
||||
for (var i = 0; i < m_RaceNames.Length; ++i)
|
||||
{
|
||||
if (Insensitive.Equals(m_RaceNames[i], value))
|
||||
if (m_RaceNames[i].InsensitiveEquals(value))
|
||||
{
|
||||
return m_RaceValues[i];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ namespace Server
|
|||
{
|
||||
m_PrefixStrings = prefixStr;
|
||||
m_Encoding = Utility.UTF8;
|
||||
_buffer = new byte[BufferSize];
|
||||
_buffer = GC.AllocateUninitializedArray<byte>(BufferSize);
|
||||
}
|
||||
|
||||
public virtual long Position => Index;
|
||||
|
|
|
|||
102
Projects/Server/Utilities/InsensitiveStringHelpers.cs
Normal file
102
Projects/Server/Utilities/InsensitiveStringHelpers.cs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright (C) 2019-2020 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: InsensitiveStringHelpers.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public static class InsensitiveStringHelpers
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int InsensitiveCompare(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.CompareTo(b, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int InsensitiveCompare(this string a, string b) => StringComparer.Ordinal.Compare(a, b);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InsensitiveEquals(this ReadOnlySpan<char> a, string b) =>
|
||||
a.Equals(b, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InsensitiveEquals(this string a, string b) =>
|
||||
a?.Equals(b, StringComparison.OrdinalIgnoreCase) ?? b == null;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InsensitiveStartsWith(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.StartsWith(b, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InsensitiveStartsWith(this string a, string b) =>
|
||||
a?.StartsWith(b, StringComparison.OrdinalIgnoreCase) == true;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InsensitiveEndsWith(this string a, string b) =>
|
||||
a?.EndsWith(b, StringComparison.OrdinalIgnoreCase) == true;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InsensitiveEndsWith(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.EndsWith(b, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InsensitiveContains(this ReadOnlySpan<char> a, string b) =>
|
||||
a.Contains(b, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InsensitiveContains(this string a, string b) =>
|
||||
a?.Contains(b, StringComparison.OrdinalIgnoreCase) == true;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InsensitiveContains(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.Contains(b, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InsensitiveContains(this string a, char b) =>
|
||||
a?.Contains(b, StringComparison.OrdinalIgnoreCase) == true;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int InsensitiveIndexOf(this string a, char b) =>
|
||||
a?.IndexOf(b, StringComparison.OrdinalIgnoreCase) ?? -1;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int InsensitiveIndexOf(this string a, string b) =>
|
||||
a?.IndexOf(b, StringComparison.OrdinalIgnoreCase) ?? -1;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int InsensitiveIndexOf(this string a, string b, int startIndex) =>
|
||||
a?.IndexOf(b, startIndex, StringComparison.OrdinalIgnoreCase) ?? -1;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int InsensitiveIndexOf(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.IndexOf(b, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string InsensitiveReplace(this string a, string o, string n) =>
|
||||
a?.Replace(o, n, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InsensitiveRemove(
|
||||
this ReadOnlySpan<char> a,
|
||||
ReadOnlySpan<char> b,
|
||||
Span<char> buffer,
|
||||
out int size
|
||||
) => a.Remove(b, StringComparison.OrdinalIgnoreCase, buffer, out size);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string InsensitiveRemove(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.Remove(b, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
100
Projects/Server/Utilities/OrdinalStringHelpers.cs
Normal file
100
Projects/Server/Utilities/OrdinalStringHelpers.cs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright (C) 2019-2020 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: OrdinalStringHelpers.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public static class OrdinalStringHelpers
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int CompareOrdinal(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.CompareTo(b, StringComparison.Ordinal);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int CompareOrdinal(this string a, string b) => StringComparer.Ordinal.Compare(a, b);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EqualsOrdinal(this ReadOnlySpan<char> a, string b) =>
|
||||
a.Equals(b, StringComparison.Ordinal);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EqualsOrdinal(this string a, string b) =>
|
||||
a?.Equals(b, StringComparison.Ordinal) ?? b == null;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool StartsWithOrdinal(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.StartsWith(b, StringComparison.Ordinal);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool StartsWithOrdinal(this string a, string b) =>
|
||||
a?.StartsWith(b, StringComparison.Ordinal) == true;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EndsWithOrdinal(this string a, string b) =>
|
||||
a?.EndsWith(b, StringComparison.Ordinal) == true;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EndsWithOrdinal(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.EndsWith(b, StringComparison.Ordinal);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool ContainsOrdinal(this ReadOnlySpan<char> a, string b) =>
|
||||
a.Contains(b, StringComparison.Ordinal);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool ContainsOrdinal(this string a, string b) =>
|
||||
a?.Contains(b, StringComparison.Ordinal) == true;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool ContainsOrdinal(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.Contains(b, StringComparison.Ordinal);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool ContainsOrdinal(this string a, char b) =>
|
||||
a?.Contains(b, StringComparison.Ordinal) == true;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int IndexOfOrdinal(this string a, char b) => a?.IndexOf(b, StringComparison.Ordinal) ?? -1;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int IndexOfOrdinal(this string a, string b) => a?.IndexOf(b, StringComparison.Ordinal) ?? -1;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int IndexOfOrdinal(this string a, string b, int startIndex) =>
|
||||
a?.IndexOf(b, startIndex, StringComparison.Ordinal) ?? -1;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int IndexOfOrdinal(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.IndexOf(b, StringComparison.Ordinal);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string ReplaceOrdinal(this string a, string o, string n) =>
|
||||
a?.Replace(o, n, StringComparison.Ordinal);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string RemoveOrdinal(this string a, string b) =>
|
||||
a?.Replace(b, "", StringComparison.Ordinal);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void RemoveOrdinal(this ReadOnlySpan<char> a, ReadOnlySpan<char> b, Span<char> buffer, out int size) =>
|
||||
a.Remove(b, StringComparison.Ordinal, buffer, out size);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string RemoveOrdinal(this ReadOnlySpan<char> a, ReadOnlySpan<char> b) =>
|
||||
a.Remove(b, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
245
Projects/Server/Utilities/StringHelpers.cs
Normal file
245
Projects/Server/Utilities/StringHelpers.cs
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright (C) 2019-2020 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: StringHelpers.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public static class StringHelpers
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string DefaultIfNullOrEmpty(this string value, string def) => value?.Trim().Length > 0 ? value : def;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Remove(
|
||||
this ReadOnlySpan<char> a,
|
||||
ReadOnlySpan<char> b,
|
||||
StringComparison comparison,
|
||||
Span<char> buffer,
|
||||
out int size
|
||||
)
|
||||
{
|
||||
size = 0;
|
||||
if (a == null || a.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sliced = a;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var indexOf = sliced.IndexOf(b, comparison);
|
||||
if (indexOf == -1)
|
||||
{
|
||||
indexOf = sliced.Length;
|
||||
}
|
||||
|
||||
if (size + indexOf > buffer.Length)
|
||||
{
|
||||
throw new OutOfMemoryException(nameof(buffer));
|
||||
}
|
||||
|
||||
sliced.Slice(0, indexOf).CopyTo(buffer.Slice(size));
|
||||
size += indexOf;
|
||||
|
||||
if (indexOf == sliced.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
sliced = sliced.Slice(indexOf + 1);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string Remove(this ReadOnlySpan<char> a, ReadOnlySpan<char> b, StringComparison comparison)
|
||||
{
|
||||
if (a == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (a.Length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
Span<char> span = a.Length < 1024 ? stackalloc char[a.Length] : null;
|
||||
char[] chrs;
|
||||
if (span == null)
|
||||
{
|
||||
chrs = ArrayPool<char>.Shared.Rent(a.Length);
|
||||
span = chrs.AsSpan();
|
||||
}
|
||||
else
|
||||
{
|
||||
chrs = null;
|
||||
}
|
||||
|
||||
a.Remove(b, comparison, span, out var size);
|
||||
|
||||
var str = span.Slice(0, size).ToString();
|
||||
|
||||
if (chrs != null)
|
||||
{
|
||||
ArrayPool<char>.Shared.Return(chrs);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string Capitalize(this string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
Span<char> span = value.Length < 1024 ? stackalloc char[value.Length] : null;
|
||||
char[] chrs;
|
||||
if (span == null)
|
||||
{
|
||||
chrs = ArrayPool<char>.Shared.Rent(value.Length);
|
||||
span = chrs.AsSpan();
|
||||
}
|
||||
else
|
||||
{
|
||||
chrs = null;
|
||||
}
|
||||
|
||||
var sliced = value.AsSpan();
|
||||
// Copy over the previous span
|
||||
sliced.CopyTo(span);
|
||||
|
||||
var index = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Special case for titles - words that don't get capitalized
|
||||
if (sliced.InsensitiveStartsWith("the "))
|
||||
{
|
||||
sliced = sliced.Slice(4);
|
||||
index += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
var indexOf = sliced.IndexOf(' ');
|
||||
span[index] = char.ToUpperInvariant(sliced[0]);
|
||||
|
||||
if (indexOf == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (indexOf == sliced.Length - 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
sliced = sliced.Slice(indexOf + 1);
|
||||
index += indexOf + 1;
|
||||
}
|
||||
|
||||
var str = span.ToString();
|
||||
|
||||
if (chrs != null)
|
||||
{
|
||||
ArrayPool<char>.Shared.Return(chrs);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public static List<string> Wrap(this string value, int perLine, int maxLines)
|
||||
{
|
||||
if ((value = value?.Trim() ?? "").Length <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var span = value.AsSpan();
|
||||
var list = new List<string>(maxLines);
|
||||
var lineLength = 0;
|
||||
|
||||
while (span.Length > 0)
|
||||
{
|
||||
var spaceIndex = span.Slice(lineLength).IndexOf(' ');
|
||||
if (spaceIndex == -1)
|
||||
{
|
||||
spaceIndex = span.Length; // End of the string
|
||||
}
|
||||
|
||||
var newLineLength = lineLength + spaceIndex;
|
||||
|
||||
if (newLineLength == perLine || newLineLength == span.Length - 1)
|
||||
{
|
||||
// Could be the end of the string
|
||||
var length = span[newLineLength] == ' ' ? newLineLength - 1 : newLineLength;
|
||||
list.Add(span.Slice(0, length).ToString());
|
||||
if (list.Count == maxLines)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
span = span.Slice(newLineLength);
|
||||
lineLength = 0;
|
||||
}
|
||||
else if (newLineLength < perLine)
|
||||
{
|
||||
lineLength = newLineLength + 1;
|
||||
}
|
||||
else if (lineLength > 0 && lineLength <= perLine)
|
||||
{
|
||||
list.Add(span.Slice(0, lineLength - 1).ToString());
|
||||
if (list.Count == maxLines)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
span = span.Slice(lineLength);
|
||||
lineLength = spaceIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
lineLength = newLineLength;
|
||||
var index = 0;
|
||||
|
||||
while (index < lineLength)
|
||||
{
|
||||
lineLength -= perLine;
|
||||
|
||||
var length = perLine - (span[index] == ' ' ? 1 : 0);
|
||||
list.Add(span.Slice(index, length).ToString());
|
||||
if (list.Count == maxLines)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
index += perLine;
|
||||
}
|
||||
|
||||
span = span.Slice(lineLength);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
|
@ -11,6 +12,7 @@ using System.Runtime.InteropServices;
|
|||
using System.Text;
|
||||
using System.Xml;
|
||||
using Microsoft.Toolkit.HighPerformance.Extensions;
|
||||
using Server.Buffers;
|
||||
using Server.Random;
|
||||
|
||||
namespace Server
|
||||
|
|
@ -144,18 +146,20 @@ namespace Server
|
|||
|
||||
ReadOnlySpan<char> chars = s.AsSpan();
|
||||
|
||||
StringBuilder stringBuilder = null;
|
||||
using ValueStringBuilder sb = new ValueStringBuilder(stackalloc char[256]);
|
||||
var hasDoneAnyReplacements = false;
|
||||
|
||||
for (int i = 0, last = 0; i < chars.Length; i++)
|
||||
{
|
||||
if (!IsSafeChar(chars[i]) || stringBuilder != null && i == chars.Length - 1)
|
||||
if (!IsSafeChar(chars[i]) && i == chars.Length - 1)
|
||||
{
|
||||
(stringBuilder ??= new StringBuilder()).Append(chars.Slice(last, i - last));
|
||||
hasDoneAnyReplacements = true;
|
||||
sb.Append(chars.Slice(last, i - last));
|
||||
last = i + 1; // Skip the unsafe char
|
||||
}
|
||||
}
|
||||
|
||||
return stringBuilder?.ToString() ?? s;
|
||||
return !hasDoneAnyReplacements ? s : sb.ToString();
|
||||
}
|
||||
|
||||
public static void Separate(StringBuilder sb, string value, string separator)
|
||||
|
|
@ -175,9 +179,6 @@ namespace Server
|
|||
str = Intern(str);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string DefaultIfNullOrEmpty(this string value, string def) => value?.Trim().Length > 0 ? value : def;
|
||||
|
||||
public static IPAddress Intern(IPAddress ipAddress)
|
||||
{
|
||||
if (ipAddress == null)
|
||||
|
|
@ -301,7 +302,7 @@ namespace Server
|
|||
public static bool IPMatch(string val, IPAddress ip, out bool valid)
|
||||
{
|
||||
var family = ip.AddressFamily;
|
||||
var useIPv6 = family == AddressFamily.InterNetworkV6 || val.Contains(':', StringComparison.Ordinal);
|
||||
var useIPv6 = family == AddressFamily.InterNetworkV6 || val.ContainsOrdinal(':');
|
||||
|
||||
ip = useIPv6 ? ip.MapToIPv6() : ip.MapToIPv4();
|
||||
|
||||
|
|
@ -619,9 +620,9 @@ namespace Server
|
|||
return "";
|
||||
}
|
||||
|
||||
var hasOpen = str.Contains('<', StringComparison.Ordinal);
|
||||
var hasClose = str.Contains('>', StringComparison.Ordinal);
|
||||
var hasPound = str.Contains('#', StringComparison.Ordinal);
|
||||
var hasOpen = str.ContainsOrdinal('<');
|
||||
var hasClose = str.ContainsOrdinal('>');
|
||||
var hasPound = str.ContainsOrdinal('#');
|
||||
|
||||
if (!hasOpen && !hasClose && !hasPound)
|
||||
{
|
||||
|
|
@ -648,9 +649,9 @@ namespace Server
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static int InsensitiveCompare(string first, string second) => Insensitive.Compare(first, second);
|
||||
public static int InsensitiveCompare(string first, string second) => first.InsensitiveCompare(second);
|
||||
|
||||
public static bool InsensitiveStartsWith(string first, string second) => Insensitive.StartsWith(first, second);
|
||||
public static bool InsensitiveStartsWith(string first, string second) => first.InsensitiveStartsWith(second);
|
||||
|
||||
public static Direction GetDirection(IPoint2D from, IPoint2D to)
|
||||
{
|
||||
|
|
@ -990,33 +991,18 @@ namespace Server
|
|||
}
|
||||
}
|
||||
|
||||
public static List<TOutput> CastListContravariant<TInput, TOutput>(this IReadOnlyCollection<TInput> coll)
|
||||
where TInput : TOutput
|
||||
// Using this instead of Linq Cast<> means we can ditch the yield and enforce contravariance
|
||||
public static HashSet<TOutput> SafeConvertSet<TInput, TOutput>(this IEnumerable<TInput> coll)
|
||||
where TOutput : TInput => coll.SafeConvert<HashSet<TOutput>, TInput, TOutput>();
|
||||
|
||||
public static List<TOutput> SafeConvertList<TInput, TOutput>(this IEnumerable<TInput> coll)
|
||||
where TOutput : TInput => coll.SafeConvert<List<TOutput>, TInput, TOutput>();
|
||||
|
||||
public static TColl SafeConvert<TColl, TInput, TOutput>(this IEnumerable<TInput> coll)
|
||||
where TOutput : TInput where TColl : ICollection<TOutput>, new()
|
||||
{
|
||||
var outputList = new List<TOutput>();
|
||||
foreach (var entry in coll)
|
||||
{
|
||||
outputList.Add(entry);
|
||||
}
|
||||
var outputList = new TColl();
|
||||
|
||||
return outputList;
|
||||
}
|
||||
|
||||
public static List<TOutput> CastListCovariant<TInput, TOutput>(this IReadOnlyCollection<TInput> coll)
|
||||
where TOutput : TInput
|
||||
{
|
||||
var outputList = new List<TOutput>();
|
||||
foreach (var entry in coll)
|
||||
{
|
||||
outputList.Add((TOutput)entry);
|
||||
}
|
||||
|
||||
return outputList;
|
||||
}
|
||||
|
||||
public static List<TOutput> SafeConvertList<TInput, TOutput>(this IReadOnlyCollection<TInput> coll) where TOutput : class
|
||||
{
|
||||
var outputList = new List<TOutput>();
|
||||
foreach (var entry in coll)
|
||||
{
|
||||
if (entry is TOutput outEntry)
|
||||
|
|
@ -1055,14 +1041,14 @@ namespace Server
|
|||
return t;
|
||||
}
|
||||
|
||||
public static int ToInt32(string value)
|
||||
public static int ToInt32(ReadOnlySpan<char> value)
|
||||
{
|
||||
int i;
|
||||
|
||||
#pragma warning disable CA1806 // Do not ignore method results
|
||||
if (value.StartsWith("0x", StringComparison.Ordinal))
|
||||
if (value.StartsWithOrdinal("0x"))
|
||||
{
|
||||
int.TryParse(value.Substring(2), NumberStyles.HexNumber, null, out i);
|
||||
int.TryParse(value.Slice(2), NumberStyles.HexNumber, null, out i);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1073,14 +1059,14 @@ namespace Server
|
|||
return i;
|
||||
}
|
||||
|
||||
public static uint ToUInt32(string value)
|
||||
public static uint ToUInt32(ReadOnlySpan<char> value)
|
||||
{
|
||||
uint i;
|
||||
|
||||
#pragma warning disable CA1806 // Do not ignore method results
|
||||
if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
||||
if (value.InsensitiveStartsWith("0x"))
|
||||
{
|
||||
uint.TryParse(value.Substring(2), NumberStyles.HexNumber, null, out i);
|
||||
uint.TryParse(value.Slice(2), NumberStyles.HexNumber, null, out i);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1091,14 +1077,14 @@ namespace Server
|
|||
return i;
|
||||
}
|
||||
|
||||
public static bool ToInt32(string value, out int i) =>
|
||||
value.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
|
||||
? int.TryParse(value.Substring(2), NumberStyles.HexNumber, null, out i)
|
||||
public static bool ToInt32(ReadOnlySpan<char> value, out int i) =>
|
||||
value.InsensitiveStartsWith("0x")
|
||||
? int.TryParse(value.Slice(2), NumberStyles.HexNumber, null, out i)
|
||||
: int.TryParse(value, out i);
|
||||
|
||||
public static bool ToUInt32(string value, out uint i) =>
|
||||
value.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
|
||||
? uint.TryParse(value.Substring(2), NumberStyles.HexNumber, null, out i)
|
||||
public static bool ToUInt32(ReadOnlySpan<char> value, out uint i) =>
|
||||
value.InsensitiveStartsWith("0x")
|
||||
? uint.TryParse(value.Slice(2), NumberStyles.HexNumber, null, out i)
|
||||
: uint.TryParse(value, out i);
|
||||
|
||||
public static int GetXMLInt32(string intString, int defaultValue)
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ namespace Server
|
|||
continue;
|
||||
}
|
||||
|
||||
byte[] saveBuffer = new byte[entry.Length];
|
||||
byte[] saveBuffer = GC.AllocateUninitializedArray<byte>(entry.Length);
|
||||
reader.Read(saveBuffer, 0, entry.Length);
|
||||
t.SaveBuffer = new BufferWriter(saveBuffer, true);
|
||||
}
|
||||
|
|
@ -331,7 +331,7 @@ namespace Server
|
|||
}
|
||||
|
||||
using FileStream bin = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
var buffer = new byte[bin.Length];
|
||||
var buffer = bin.Length <= int.MaxValue ? GC.AllocateUninitializedArray<byte>((int)bin.Length) : new byte[bin.Length];
|
||||
bin.Read(buffer);
|
||||
bin.Close();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue