fix: Updates BufferWriter with more streamlined code (#1367)
### Summary Updates BufferWriter with more standard ways of writing primitives. Eliminates looping to write a string per @jaedan's suggestion. Note: This change assumes we don't have crazy large strings.
This commit is contained in:
parent
dda55889f6
commit
2d30ea4cc5
1 changed files with 29 additions and 71 deletions
|
|
@ -14,6 +14,7 @@
|
||||||
*************************************************************************/
|
*************************************************************************/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
@ -123,20 +124,15 @@ public class BufferWriter : IGenericWriter
|
||||||
|
|
||||||
public void Write(ReadOnlySpan<byte> bytes)
|
public void Write(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
var remaining = bytes.Length;
|
var length = bytes.Length;
|
||||||
var idx = 0;
|
|
||||||
|
|
||||||
while (remaining > 0)
|
while (_buffer.Length - _index < length)
|
||||||
{
|
{
|
||||||
FlushIfNeeded(remaining);
|
Flush();
|
||||||
|
|
||||||
var count = Math.Min((int)(_buffer.Length - Index), remaining);
|
|
||||||
bytes.Slice(idx, count).CopyTo(_buffer.AsSpan((int)Index, count));
|
|
||||||
|
|
||||||
idx += count;
|
|
||||||
Index += count;
|
|
||||||
remaining -= count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes.CopyTo(_buffer.AsSpan((int)_index));
|
||||||
|
Index += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
@ -200,14 +196,8 @@ public class BufferWriter : IGenericWriter
|
||||||
{
|
{
|
||||||
FlushIfNeeded(8);
|
FlushIfNeeded(8);
|
||||||
|
|
||||||
_buffer[Index++] = (byte)value;
|
BinaryPrimitives.WriteInt64LittleEndian(_buffer.AsSpan((int)_index), value);
|
||||||
_buffer[Index++] = (byte)(value >> 8);
|
Index += 8;
|
||||||
_buffer[Index++] = (byte)(value >> 16);
|
|
||||||
_buffer[Index++] = (byte)(value >> 24);
|
|
||||||
_buffer[Index++] = (byte)(value >> 32);
|
|
||||||
_buffer[Index++] = (byte)(value >> 40);
|
|
||||||
_buffer[Index++] = (byte)(value >> 48);
|
|
||||||
_buffer[Index++] = (byte)(value >> 56);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
@ -215,14 +205,8 @@ public class BufferWriter : IGenericWriter
|
||||||
{
|
{
|
||||||
FlushIfNeeded(8);
|
FlushIfNeeded(8);
|
||||||
|
|
||||||
_buffer[Index++] = (byte)value;
|
BinaryPrimitives.WriteUInt64LittleEndian(_buffer.AsSpan((int)_index), value);
|
||||||
_buffer[Index++] = (byte)(value >> 8);
|
Index += 8;
|
||||||
_buffer[Index++] = (byte)(value >> 16);
|
|
||||||
_buffer[Index++] = (byte)(value >> 24);
|
|
||||||
_buffer[Index++] = (byte)(value >> 32);
|
|
||||||
_buffer[Index++] = (byte)(value >> 40);
|
|
||||||
_buffer[Index++] = (byte)(value >> 48);
|
|
||||||
_buffer[Index++] = (byte)(value >> 56);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
@ -230,10 +214,8 @@ public class BufferWriter : IGenericWriter
|
||||||
{
|
{
|
||||||
FlushIfNeeded(4);
|
FlushIfNeeded(4);
|
||||||
|
|
||||||
_buffer[Index++] = (byte)value;
|
BinaryPrimitives.WriteInt32LittleEndian(_buffer.AsSpan((int)_index), value);
|
||||||
_buffer[Index++] = (byte)(value >> 8);
|
Index += 4;
|
||||||
_buffer[Index++] = (byte)(value >> 16);
|
|
||||||
_buffer[Index++] = (byte)(value >> 24);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
@ -241,10 +223,8 @@ public class BufferWriter : IGenericWriter
|
||||||
{
|
{
|
||||||
FlushIfNeeded(4);
|
FlushIfNeeded(4);
|
||||||
|
|
||||||
_buffer[Index++] = (byte)value;
|
BinaryPrimitives.WriteUInt32LittleEndian(_buffer.AsSpan((int)_index), value);
|
||||||
_buffer[Index++] = (byte)(value >> 8);
|
Index += 4;
|
||||||
_buffer[Index++] = (byte)(value >> 16);
|
|
||||||
_buffer[Index++] = (byte)(value >> 24);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
@ -252,8 +232,8 @@ public class BufferWriter : IGenericWriter
|
||||||
{
|
{
|
||||||
FlushIfNeeded(2);
|
FlushIfNeeded(2);
|
||||||
|
|
||||||
_buffer[Index++] = (byte)value;
|
BinaryPrimitives.WriteInt16LittleEndian(_buffer.AsSpan((int)_index), value);
|
||||||
_buffer[Index++] = (byte)(value >> 8);
|
Index += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
@ -261,20 +241,16 @@ public class BufferWriter : IGenericWriter
|
||||||
{
|
{
|
||||||
FlushIfNeeded(2);
|
FlushIfNeeded(2);
|
||||||
|
|
||||||
_buffer[Index++] = (byte)value;
|
BinaryPrimitives.WriteUInt16LittleEndian(_buffer.AsSpan((int)_index), value);
|
||||||
_buffer[Index++] = (byte)(value >> 8);
|
Index += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public unsafe void Write(double value)
|
public void Write(double value)
|
||||||
{
|
{
|
||||||
FlushIfNeeded(8);
|
FlushIfNeeded(8);
|
||||||
|
|
||||||
fixed (byte* pBuffer = _buffer)
|
BinaryPrimitives.WriteDoubleLittleEndian(_buffer.AsSpan((int)_index), value);
|
||||||
{
|
|
||||||
*(double*)(pBuffer + Index) = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Index += 8;
|
Index += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -283,12 +259,8 @@ public class BufferWriter : IGenericWriter
|
||||||
{
|
{
|
||||||
FlushIfNeeded(4);
|
FlushIfNeeded(4);
|
||||||
|
|
||||||
fixed (byte* pBuffer = _buffer)
|
BinaryPrimitives.WriteSingleLittleEndian(_buffer.AsSpan((int)_index), value);
|
||||||
{
|
Index += 8;
|
||||||
*(float*)(pBuffer + Index) = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Index += 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
@ -333,30 +305,16 @@ public class BufferWriter : IGenericWriter
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal void InternalWriteString(string value)
|
internal void InternalWriteString(string value)
|
||||||
{
|
{
|
||||||
var remaining = _encoding.GetByteCount(value);
|
var length = _encoding.GetByteCount(value);
|
||||||
|
|
||||||
((IGenericWriter)this).WriteEncodedInt(remaining);
|
((IGenericWriter)this).WriteEncodedInt(length);
|
||||||
|
|
||||||
if (remaining == 0)
|
while (_buffer.Length - _index < length)
|
||||||
{
|
{
|
||||||
return;
|
Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is much faster to encode to stack buffer, then copy to the real buffer
|
// We don't use spans here since that incurs extra allocations for safety.
|
||||||
Span<byte> span = stackalloc byte[Math.Min(BufferSize, 256)];
|
Index += _encoding.GetBytes(value, 0, value.Length, _buffer, (int)_index);
|
||||||
var maxChars = span.Length / _encoding.GetMaxByteCount(1);
|
|
||||||
var charsLeft = value.Length;
|
|
||||||
var current = 0;
|
|
||||||
|
|
||||||
while (charsLeft > 0)
|
|
||||||
{
|
|
||||||
var charCount = Math.Min(charsLeft, maxChars);
|
|
||||||
var bytesWritten = _encoding.GetBytes(value.AsSpan(current, charCount), span);
|
|
||||||
remaining -= bytesWritten;
|
|
||||||
charsLeft -= charCount;
|
|
||||||
current += charCount;
|
|
||||||
|
|
||||||
Write(span[..bytesWritten]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue