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:
Kamron Batman 2023-03-10 00:05:16 -08:00 committed by GitHub
parent dda55889f6
commit 2d30ea4cc5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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]);
}
} }
} }