Fix Content.Benchmark warnings (#17771)
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using Content.IntegrationTests;
|
using Content.IntegrationTests;
|
||||||
@@ -9,7 +8,6 @@ using Content.Server.DeviceNetwork.Systems;
|
|||||||
using Robust.Shared;
|
using Robust.Shared;
|
||||||
using Robust.Shared.Analyzers;
|
using Robust.Shared.Analyzers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
namespace Content.Benchmarks;
|
namespace Content.Benchmarks;
|
||||||
@@ -23,8 +21,8 @@ public class DeviceNetworkingBenchmark
|
|||||||
private DeviceNetworkSystem _deviceNetworkSystem = default!;
|
private DeviceNetworkSystem _deviceNetworkSystem = default!;
|
||||||
private EntityUid _sourceEntity;
|
private EntityUid _sourceEntity;
|
||||||
private EntityUid _sourceWirelessEntity;
|
private EntityUid _sourceWirelessEntity;
|
||||||
private List<EntityUid> _targetEntities = new();
|
private readonly List<EntityUid> _targetEntities = new();
|
||||||
private List<EntityUid> _targetWirelessEntities = new();
|
private readonly List<EntityUid> _targetWirelessEntities = new();
|
||||||
|
|
||||||
|
|
||||||
private NetworkPayload _payload = default!;
|
private NetworkPayload _payload = default!;
|
||||||
@@ -58,7 +56,7 @@ public class DeviceNetworkingBenchmark
|
|||||||
public async Task SetupAsync()
|
public async Task SetupAsync()
|
||||||
{
|
{
|
||||||
ProgramShared.PathOffset = "../../../../";
|
ProgramShared.PathOffset = "../../../../";
|
||||||
_pair = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
|
_pair = await PoolManager.GetServerClient(new PoolSettings { NoClient = true, ExtraPrototypes = Prototypes });
|
||||||
var server = _pair.Pair.Server;
|
var server = _pair.Pair.Server;
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace Content.Benchmarks
|
|||||||
[Virtual]
|
[Virtual]
|
||||||
public class DynamicTreeBenchmark
|
public class DynamicTreeBenchmark
|
||||||
{
|
{
|
||||||
private static readonly Box2[] _aabbs1 =
|
private static readonly Box2[] Aabbs1 =
|
||||||
{
|
{
|
||||||
((Box2) default).Enlarged(1), //2x2 square
|
((Box2) default).Enlarged(1), //2x2 square
|
||||||
((Box2) default).Enlarged(2), //4x4 square
|
((Box2) default).Enlarged(2), //4x4 square
|
||||||
@@ -39,11 +39,11 @@ namespace Content.Benchmarks
|
|||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
_b2Tree = new B2DynamicTree<int>();
|
_b2Tree = new B2DynamicTree<int>();
|
||||||
_tree = new DynamicTree<int>((in int value) => _aabbs1[value], capacity: 16);
|
_tree = new DynamicTree<int>((in int value) => Aabbs1[value], capacity: 16);
|
||||||
|
|
||||||
for (var i = 0; i < _aabbs1.Length; i++)
|
for (var i = 0; i < Aabbs1.Length; i++)
|
||||||
{
|
{
|
||||||
var aabb = _aabbs1[i];
|
var aabb = Aabbs1[i];
|
||||||
_b2Tree.CreateProxy(aabb, i);
|
_b2Tree.CreateProxy(aabb, i);
|
||||||
_tree.Add(i);
|
_tree.Add(i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,9 +189,7 @@ namespace Content.Benchmarks
|
|||||||
public TEntity GetEntity(TEntityUid entityUid)
|
public TEntity GetEntity(TEntityUid entityUid)
|
||||||
{
|
{
|
||||||
if (!TryGetEntity(entityUid, out var entity))
|
if (!TryGetEntity(entityUid, out var entity))
|
||||||
{
|
throw new ArgumentException($"Failed to get entity {entityUid} from storage.");
|
||||||
throw new ArgumentException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
@@ -200,7 +198,7 @@ namespace Content.Benchmarks
|
|||||||
private sealed class GenEntityStorage : EntityStorage<GenEntity, GenEntityUid>
|
private sealed class GenEntityStorage : EntityStorage<GenEntity, GenEntityUid>
|
||||||
{
|
{
|
||||||
private (int generation, GenEntity entity)[] _entities = new (int, GenEntity)[1];
|
private (int generation, GenEntity entity)[] _entities = new (int, GenEntity)[1];
|
||||||
private readonly List<int> _availableSlots = new() {0};
|
private readonly List<int> _availableSlots = new() { 0 };
|
||||||
|
|
||||||
public override bool TryGetEntity(GenEntityUid entityUid, out GenEntity entity)
|
public override bool TryGetEntity(GenEntityUid entityUid, out GenEntity entity)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace Content.Benchmarks
|
|||||||
var componentFactory = new Mock<IComponentFactory>();
|
var componentFactory = new Mock<IComponentFactory>();
|
||||||
componentFactory.Setup(p => p.GetComponent<DummyComponent>()).Returns(new DummyComponent());
|
componentFactory.Setup(p => p.GetComponent<DummyComponent>()).Returns(new DummyComponent());
|
||||||
componentFactory.Setup(p => p.GetRegistration(It.IsAny<DummyComponent>())).Returns(dummyReg);
|
componentFactory.Setup(p => p.GetRegistration(It.IsAny<DummyComponent>())).Returns(dummyReg);
|
||||||
componentFactory.Setup(p => p.GetAllRefTypes()).Returns(new[] {CompIdx.Index<DummyComponent>()});
|
componentFactory.Setup(p => p.GetAllRefTypes()).Returns(new[] { CompIdx.Index<DummyComponent>() });
|
||||||
|
|
||||||
IoCManager.RegisterInstance<IComponentFactory>(componentFactory.Object);
|
IoCManager.RegisterInstance<IComponentFactory>(componentFactory.Object);
|
||||||
|
|
||||||
|
|||||||
@@ -45,9 +45,10 @@ public class MapLoadBenchmark
|
|||||||
|
|
||||||
public static IEnumerable<string> MapsSource { get; set; }
|
public static IEnumerable<string> MapsSource { get; set; }
|
||||||
|
|
||||||
[ParamsSource(nameof(MapsSource))] public string Map;
|
[ParamsSource(nameof(MapsSource))]
|
||||||
|
public string Map;
|
||||||
|
|
||||||
public static Dictionary<string, string> Paths;
|
public Dictionary<string, string> Paths;
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public async Task LoadMap()
|
public async Task LoadMap()
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ namespace Content.Benchmarks
|
|||||||
{
|
{
|
||||||
private MemoryStream _writeStream;
|
private MemoryStream _writeStream;
|
||||||
private MemoryStream _readStream;
|
private MemoryStream _readStream;
|
||||||
private ushort _x16 = 5;
|
private readonly ushort _x16 = 5;
|
||||||
private uint _x32 = 5;
|
private readonly uint _x32 = 5;
|
||||||
private ulong _x64 = 5;
|
private readonly ulong _x64 = 5;
|
||||||
private ushort _read16;
|
private ushort _read16;
|
||||||
private uint _read32;
|
private uint _read32;
|
||||||
private ulong _read64;
|
private ulong _read64;
|
||||||
@@ -24,7 +24,7 @@ namespace Content.Benchmarks
|
|||||||
{
|
{
|
||||||
_writeStream = new MemoryStream(64);
|
_writeStream = new MemoryStream(64);
|
||||||
_readStream = new MemoryStream();
|
_readStream = new MemoryStream();
|
||||||
_readStream.Write(new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8});
|
_readStream.Write(new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8 });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace Content.Benchmarks
|
|||||||
public void BenchReadCore()
|
public void BenchReadCore()
|
||||||
{
|
{
|
||||||
_inputStream.Position = 0;
|
_inputStream.Position = 0;
|
||||||
ReadPrimitiveCore(_inputStream, out string _);
|
ReadPrimitiveCore(_inputStream, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
@@ -62,7 +62,7 @@ namespace Content.Benchmarks
|
|||||||
public void BenchReadUnsafe()
|
public void BenchReadUnsafe()
|
||||||
{
|
{
|
||||||
_inputStream.Position = 0;
|
_inputStream.Position = 0;
|
||||||
ReadPrimitiveUnsafe(_inputStream, out string _);
|
ReadPrimitiveUnsafe(_inputStream, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
@@ -76,329 +76,356 @@ namespace Content.Benchmarks
|
|||||||
public void BenchReadSlow()
|
public void BenchReadSlow()
|
||||||
{
|
{
|
||||||
_inputStream.Position = 0;
|
_inputStream.Position = 0;
|
||||||
ReadPrimitiveSlow(_inputStream, out string _);
|
ReadPrimitiveSlow(_inputStream, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WritePrimitiveCore(Stream stream, string value)
|
public static void WritePrimitiveCore(Stream stream, string value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
Primitives.WritePrimitive(stream, (uint)0);
|
Primitives.WritePrimitive(stream, (uint) 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.Length == 0)
|
if (value.Length == 0)
|
||||||
{
|
{
|
||||||
Primitives.WritePrimitive(stream, (uint)1);
|
Primitives.WritePrimitive(stream, (uint) 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<byte> buf = stackalloc byte[StringByteBufferLength];
|
Span<byte> buf = stackalloc byte[StringByteBufferLength];
|
||||||
|
|
||||||
var totalChars = value.Length;
|
var totalChars = value.Length;
|
||||||
var totalBytes = Encoding.UTF8.GetByteCount(value);
|
var totalBytes = Encoding.UTF8.GetByteCount(value);
|
||||||
|
|
||||||
Primitives.WritePrimitive(stream, (uint)totalBytes + 1);
|
Primitives.WritePrimitive(stream, (uint) totalBytes + 1);
|
||||||
Primitives.WritePrimitive(stream, (uint)totalChars);
|
Primitives.WritePrimitive(stream, (uint) totalChars);
|
||||||
|
|
||||||
var totalRead = 0;
|
var totalRead = 0;
|
||||||
ReadOnlySpan<char> span = value;
|
ReadOnlySpan<char> span = value;
|
||||||
for (;;)
|
while (true)
|
||||||
{
|
{
|
||||||
var finalChunk = totalRead + totalChars >= totalChars;
|
var finalChunk = totalRead + totalChars >= totalChars;
|
||||||
Utf8.FromUtf16(span, buf, out var read, out var wrote, isFinalBlock: finalChunk);
|
Utf8.FromUtf16(span, buf, out var read, out var wrote, isFinalBlock: finalChunk);
|
||||||
stream.Write(buf.Slice(0, wrote));
|
stream.Write(buf[0..wrote]);
|
||||||
totalRead += read;
|
totalRead += read;
|
||||||
if (read >= totalChars)
|
if (read >= totalChars)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
span = span[read..];
|
span = span[read..];
|
||||||
totalChars -= read;
|
totalChars -= read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly SpanAction<char, (int, Stream)> _stringSpanRead = StringSpanRead;
|
public static void ReadPrimitiveCore(Stream stream, out string value)
|
||||||
|
{
|
||||||
|
Primitives.ReadPrimitive(stream, out uint totalBytes);
|
||||||
|
|
||||||
public static void ReadPrimitiveCore(Stream stream, out string value)
|
if (totalBytes == 0)
|
||||||
{
|
{
|
||||||
Primitives.ReadPrimitive(stream, out uint totalBytes);
|
value = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (totalBytes == 0)
|
if (totalBytes == 1)
|
||||||
{
|
{
|
||||||
value = null;
|
value = string.Empty;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalBytes == 1)
|
totalBytes -= 1;
|
||||||
{
|
|
||||||
value = string.Empty;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
totalBytes -= 1;
|
|
||||||
|
|
||||||
Primitives.ReadPrimitive(stream, out uint totalChars);
|
Primitives.ReadPrimitive(stream, out uint totalChars);
|
||||||
|
|
||||||
value = string.Create((int) totalChars, ((int) totalBytes, stream), _stringSpanRead);
|
value = string.Create((int) totalChars, ((int) totalBytes, stream), StringSpanRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StringSpanRead(Span<char> span, (int totalBytes, Stream stream) tuple)
|
private static void StringSpanRead(Span<char> span, (int totalBytes, Stream stream) tuple)
|
||||||
{
|
{
|
||||||
Span<byte> buf = stackalloc byte[StringByteBufferLength];
|
Span<byte> buf = stackalloc byte[StringByteBufferLength];
|
||||||
|
|
||||||
// ReSharper disable VariableHidesOuterVariable
|
// ReSharper disable VariableHidesOuterVariable
|
||||||
var (totalBytes, stream) = tuple;
|
var (totalBytes, stream) = tuple;
|
||||||
// ReSharper restore VariableHidesOuterVariable
|
// ReSharper restore VariableHidesOuterVariable
|
||||||
|
|
||||||
var totalBytesRead = 0;
|
var totalBytesRead = 0;
|
||||||
var totalCharsRead = 0;
|
var totalCharsRead = 0;
|
||||||
var writeBufStart = 0;
|
var writeBufStart = 0;
|
||||||
|
|
||||||
while (totalBytesRead < totalBytes)
|
while (totalBytesRead < totalBytes)
|
||||||
{
|
{
|
||||||
var bytesLeft = totalBytes - totalBytesRead;
|
var bytesLeft = totalBytes - totalBytesRead;
|
||||||
var bytesReadLeft = Math.Min(buf.Length, bytesLeft);
|
var bytesReadLeft = Math.Min(buf.Length, bytesLeft);
|
||||||
var writeSlice = buf.Slice(writeBufStart, bytesReadLeft - writeBufStart);
|
var writeSlice = buf[writeBufStart..(bytesReadLeft - writeBufStart)];
|
||||||
var bytesInBuffer = stream.Read(writeSlice);
|
var bytesInBuffer = stream.Read(writeSlice);
|
||||||
if (bytesInBuffer == 0) throw new EndOfStreamException();
|
if (bytesInBuffer == 0) throw new EndOfStreamException();
|
||||||
|
|
||||||
var readFromStream = bytesInBuffer + writeBufStart;
|
var readFromStream = bytesInBuffer + writeBufStart;
|
||||||
var final = readFromStream == bytesLeft;
|
var final = readFromStream == bytesLeft;
|
||||||
var status = Utf8.ToUtf16(buf[..readFromStream], span[totalCharsRead..], out var bytesRead, out var charsRead, isFinalBlock: final);
|
var status = Utf8.ToUtf16(buf[..readFromStream], span[totalCharsRead..], out var bytesRead, out var charsRead, isFinalBlock: final);
|
||||||
|
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
totalCharsRead += charsRead;
|
totalCharsRead += charsRead;
|
||||||
writeBufStart = 0;
|
writeBufStart = 0;
|
||||||
|
|
||||||
if (status == OperationStatus.DestinationTooSmall)
|
if (status == OperationStatus.DestinationTooSmall)
|
||||||
{
|
{
|
||||||
// Malformed data?
|
// Malformed data?
|
||||||
throw new InvalidDataException();
|
throw new InvalidDataException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == OperationStatus.NeedMoreData)
|
if (status == OperationStatus.NeedMoreData)
|
||||||
{
|
{
|
||||||
// We got cut short in the middle of a multi-byte UTF-8 sequence.
|
// We got cut short in the middle of a multi-byte UTF-8 sequence.
|
||||||
// So we need to move it to the bottom of the span, then read the next bit *past* that.
|
// So we need to move it to the bottom of the span, then read the next bit *past* that.
|
||||||
// This copy should be fine because we're only ever gonna be copying up to 4 bytes
|
// This copy should be fine because we're only ever gonna be copying up to 4 bytes
|
||||||
// from the end of the buffer to the start.
|
// from the end of the buffer to the start.
|
||||||
// So no chance of overlap.
|
// So no chance of overlap.
|
||||||
buf[bytesRead..].CopyTo(buf);
|
buf[bytesRead..].CopyTo(buf);
|
||||||
writeBufStart = bytesReadLeft - bytesRead;
|
writeBufStart = bytesReadLeft - bytesRead;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Assert(status == OperationStatus.Done);
|
Debug.Assert(status == OperationStatus.Done);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WritePrimitiveSlow(Stream stream, string value)
|
public static void WritePrimitiveSlow(Stream stream, string value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
Primitives.WritePrimitive(stream, (uint)0);
|
Primitives.WritePrimitive(stream, (uint) 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (value.Length == 0)
|
else if (value.Length == 0)
|
||||||
{
|
{
|
||||||
Primitives.WritePrimitive(stream, (uint)1);
|
Primitives.WritePrimitive(stream, (uint) 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var encoding = new UTF8Encoding(false, true);
|
var encoding = new UTF8Encoding(false, true);
|
||||||
|
|
||||||
int len = encoding.GetByteCount(value);
|
var len = encoding.GetByteCount(value);
|
||||||
|
|
||||||
Primitives.WritePrimitive(stream, (uint)len + 1);
|
Primitives.WritePrimitive(stream, (uint) len + 1);
|
||||||
Primitives.WritePrimitive(stream, (uint)value.Length);
|
Primitives.WritePrimitive(stream, (uint) value.Length);
|
||||||
|
|
||||||
var buf = new byte[len];
|
var buf = new byte[len];
|
||||||
|
|
||||||
encoding.GetBytes(value, 0, value.Length, buf, 0);
|
encoding.GetBytes(value, 0, value.Length, buf, 0);
|
||||||
|
|
||||||
stream.Write(buf, 0, len);
|
stream.Write(buf, 0, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReadPrimitiveSlow(Stream stream, out string value)
|
public static void ReadPrimitiveSlow(Stream stream, out string value)
|
||||||
{
|
{
|
||||||
uint len;
|
Primitives.ReadPrimitive(stream, out uint len);
|
||||||
Primitives.ReadPrimitive(stream, out len);
|
|
||||||
|
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
{
|
{
|
||||||
value = null;
|
value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (len == 1)
|
else if (len == 1)
|
||||||
{
|
{
|
||||||
value = string.Empty;
|
value = string.Empty;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint totalChars;
|
Primitives.ReadPrimitive(stream, out uint _);
|
||||||
Primitives.ReadPrimitive(stream, out totalChars);
|
|
||||||
|
|
||||||
len -= 1;
|
len -= 1;
|
||||||
|
|
||||||
var encoding = new UTF8Encoding(false, true);
|
var encoding = new UTF8Encoding(false, true);
|
||||||
|
|
||||||
var buf = new byte[len];
|
var buf = new byte[len];
|
||||||
|
|
||||||
int l = 0;
|
var l = 0;
|
||||||
|
|
||||||
while (l < len)
|
while (l < len)
|
||||||
{
|
{
|
||||||
int r = stream.Read(buf, l, (int)len - l);
|
var r = stream.Read(buf, l, (int) len - l);
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
throw new EndOfStreamException();
|
throw new EndOfStreamException();
|
||||||
l += r;
|
l += r;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = encoding.GetString(buf);
|
value = encoding.GetString(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class StringHelper
|
private sealed class StringHelper
|
||||||
{
|
{
|
||||||
public StringHelper()
|
public StringHelper()
|
||||||
{
|
{
|
||||||
this.Encoding = new UTF8Encoding(false, true);
|
Encoding = new UTF8Encoding(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder m_encoder;
|
private Encoder _encoder;
|
||||||
Decoder m_decoder;
|
private Decoder _decoder;
|
||||||
|
|
||||||
byte[] m_byteBuffer;
|
private byte[] _byteBuffer;
|
||||||
char[] m_charBuffer;
|
private char[] _charBuffer;
|
||||||
|
|
||||||
public UTF8Encoding Encoding { get; private set; }
|
public UTF8Encoding Encoding { get; private set; }
|
||||||
public Encoder Encoder { get { if (m_encoder == null) m_encoder = this.Encoding.GetEncoder(); return m_encoder; } }
|
public Encoder Encoder
|
||||||
public Decoder Decoder { get { if (m_decoder == null) m_decoder = this.Encoding.GetDecoder(); return m_decoder; } }
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
_encoder ??= Encoding.GetEncoder();
|
||||||
|
return _encoder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Decoder Decoder
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
_decoder ??= Encoding.GetDecoder();
|
||||||
|
return _decoder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] ByteBuffer { get { if (m_byteBuffer == null) m_byteBuffer = new byte[StringByteBufferLength]; return m_byteBuffer; } }
|
public byte[] ByteBuffer
|
||||||
public char[] CharBuffer { get { if (m_charBuffer == null) m_charBuffer = new char[StringCharBufferLength]; return m_charBuffer; } }
|
{
|
||||||
}
|
get
|
||||||
|
{
|
||||||
|
_byteBuffer ??= new byte[StringByteBufferLength];
|
||||||
|
return _byteBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public char[] CharBuffer
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
_charBuffer ??= new char[StringCharBufferLength];
|
||||||
|
return _charBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
static StringHelper s_stringHelper;
|
private static StringHelper _stringHelper;
|
||||||
|
|
||||||
public unsafe static void WritePrimitiveUnsafe(Stream stream, string value)
|
public static unsafe void WritePrimitiveUnsafe(Stream stream, string value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
Primitives.WritePrimitive(stream, (uint)0);
|
Primitives.WritePrimitive(stream, (uint) 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (value.Length == 0)
|
else if (value.Length == 0)
|
||||||
{
|
{
|
||||||
Primitives.WritePrimitive(stream, (uint)1);
|
Primitives.WritePrimitive(stream, (uint) 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var helper = s_stringHelper;
|
var helper = _stringHelper;
|
||||||
if (helper == null)
|
if (helper == null)
|
||||||
s_stringHelper = helper = new StringHelper();
|
_stringHelper = helper = new StringHelper();
|
||||||
|
|
||||||
var encoder = helper.Encoder;
|
var encoder = helper.Encoder;
|
||||||
var buf = helper.ByteBuffer;
|
var buf = helper.ByteBuffer;
|
||||||
|
|
||||||
int totalChars = value.Length;
|
var totalChars = value.Length;
|
||||||
int totalBytes;
|
int totalBytes;
|
||||||
|
|
||||||
fixed (char* ptr = value)
|
fixed (char* ptr = value)
|
||||||
totalBytes = encoder.GetByteCount(ptr, totalChars, true);
|
totalBytes = encoder.GetByteCount(ptr, totalChars, true);
|
||||||
|
|
||||||
Primitives.WritePrimitive(stream, (uint)totalBytes + 1);
|
Primitives.WritePrimitive(stream, (uint) totalBytes + 1);
|
||||||
Primitives.WritePrimitive(stream, (uint)totalChars);
|
Primitives.WritePrimitive(stream, (uint) totalChars);
|
||||||
|
|
||||||
int p = 0;
|
var p = 0;
|
||||||
bool completed = false;
|
var completed = false;
|
||||||
|
|
||||||
while (completed == false)
|
while (completed == false)
|
||||||
{
|
{
|
||||||
int charsConverted;
|
int charsConverted;
|
||||||
int bytesConverted;
|
int bytesConverted;
|
||||||
|
|
||||||
fixed (char* src = value)
|
fixed (char* src = value)
|
||||||
fixed (byte* dst = buf)
|
fixed (byte* dst = buf)
|
||||||
{
|
{
|
||||||
encoder.Convert(src + p, totalChars - p, dst, buf.Length, true,
|
encoder.Convert(src + p, totalChars - p, dst, buf.Length, true,
|
||||||
out charsConverted, out bytesConverted, out completed);
|
out charsConverted, out bytesConverted, out completed);
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.Write(buf, 0, bytesConverted);
|
stream.Write(buf, 0, bytesConverted);
|
||||||
|
|
||||||
p += charsConverted;
|
p += charsConverted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReadPrimitiveUnsafe(Stream stream, out string value)
|
public static void ReadPrimitiveUnsafe(Stream stream, out string value)
|
||||||
{
|
{
|
||||||
uint totalBytes;
|
Primitives.ReadPrimitive(stream, out uint totalBytes);
|
||||||
Primitives.ReadPrimitive(stream, out totalBytes);
|
|
||||||
|
|
||||||
if (totalBytes == 0)
|
if (totalBytes == 0)
|
||||||
{
|
{
|
||||||
value = null;
|
value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (totalBytes == 1)
|
else if (totalBytes == 1)
|
||||||
{
|
{
|
||||||
value = string.Empty;
|
value = string.Empty;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
totalBytes -= 1;
|
totalBytes -= 1;
|
||||||
|
|
||||||
uint totalChars;
|
Primitives.ReadPrimitive(stream, out uint totalChars);
|
||||||
Primitives.ReadPrimitive(stream, out totalChars);
|
|
||||||
|
|
||||||
var helper = s_stringHelper;
|
var helper = _stringHelper;
|
||||||
if (helper == null)
|
if (helper == null)
|
||||||
s_stringHelper = helper = new StringHelper();
|
_stringHelper = helper = new StringHelper();
|
||||||
|
|
||||||
var decoder = helper.Decoder;
|
var decoder = helper.Decoder;
|
||||||
var buf = helper.ByteBuffer;
|
var buf = helper.ByteBuffer;
|
||||||
char[] chars;
|
char[] chars;
|
||||||
if (totalChars <= StringCharBufferLength)
|
if (totalChars <= StringCharBufferLength)
|
||||||
chars = helper.CharBuffer;
|
chars = helper.CharBuffer;
|
||||||
else
|
else
|
||||||
chars = new char[totalChars];
|
chars = new char[totalChars];
|
||||||
|
|
||||||
int streamBytesLeft = (int)totalBytes;
|
var streamBytesLeft = (int) totalBytes;
|
||||||
|
|
||||||
int cp = 0;
|
var cp = 0;
|
||||||
|
|
||||||
while (streamBytesLeft > 0)
|
while (streamBytesLeft > 0)
|
||||||
{
|
{
|
||||||
int bytesInBuffer = stream.Read(buf, 0, Math.Min(buf.Length, streamBytesLeft));
|
var bytesInBuffer = stream.Read(buf, 0, Math.Min(buf.Length, streamBytesLeft));
|
||||||
if (bytesInBuffer == 0)
|
if (bytesInBuffer == 0)
|
||||||
throw new EndOfStreamException();
|
throw new EndOfStreamException();
|
||||||
|
|
||||||
streamBytesLeft -= bytesInBuffer;
|
streamBytesLeft -= bytesInBuffer;
|
||||||
bool flush = streamBytesLeft == 0;
|
var flush = streamBytesLeft == 0;
|
||||||
|
|
||||||
bool completed = false;
|
var completed = false;
|
||||||
|
|
||||||
int p = 0;
|
var p = 0;
|
||||||
|
|
||||||
while (completed == false)
|
while (completed == false)
|
||||||
{
|
{
|
||||||
int charsConverted;
|
decoder.Convert(
|
||||||
int bytesConverted;
|
buf,
|
||||||
|
p,
|
||||||
|
bytesInBuffer - p,
|
||||||
|
chars,
|
||||||
|
cp,
|
||||||
|
(int) totalChars - cp,
|
||||||
|
flush,
|
||||||
|
out var bytesConverted,
|
||||||
|
out var charsConverted,
|
||||||
|
out completed
|
||||||
|
);
|
||||||
|
|
||||||
decoder.Convert(buf, p, bytesInBuffer - p,
|
p += bytesConverted;
|
||||||
chars, cp, (int)totalChars - cp,
|
cp += charsConverted;
|
||||||
flush,
|
}
|
||||||
out bytesConverted, out charsConverted, out completed);
|
}
|
||||||
|
|
||||||
p += bytesConverted;
|
value = new string(chars, 0, (int) totalChars);
|
||||||
cp += charsConverted;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value = new string(chars, 0, (int)totalChars);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user