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.Buffers.Binary;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
@ -123,20 +124,15 @@ public class BufferWriter : IGenericWriter
public void Write(ReadOnlySpan<byte> bytes)
{
var remaining = bytes.Length;
var idx = 0;
var length = bytes.Length;
while (remaining > 0)
while (_buffer.Length - _index < length)
{
FlushIfNeeded(remaining);
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;
Flush();
}
bytes.CopyTo(_buffer.AsSpan((int)_index));
Index += length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -200,14 +196,8 @@ public class BufferWriter : IGenericWriter
{
FlushIfNeeded(8);
_buffer[Index++] = (byte)value;
_buffer[Index++] = (byte)(value >> 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);
BinaryPrimitives.WriteInt64LittleEndian(_buffer.AsSpan((int)_index), value);
Index += 8;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -215,14 +205,8 @@ public class BufferWriter : IGenericWriter
{
FlushIfNeeded(8);
_buffer[Index++] = (byte)value;
_buffer[Index++] = (byte)(value >> 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);
BinaryPrimitives.WriteUInt64LittleEndian(_buffer.AsSpan((int)_index), value);
Index += 8;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -230,10 +214,8 @@ public class BufferWriter : IGenericWriter
{
FlushIfNeeded(4);
_buffer[Index++] = (byte)value;
_buffer[Index++] = (byte)(value >> 8);
_buffer[Index++] = (byte)(value >> 16);
_buffer[Index++] = (byte)(value >> 24);
BinaryPrimitives.WriteInt32LittleEndian(_buffer.AsSpan((int)_index), value);
Index += 4;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -241,10 +223,8 @@ public class BufferWriter : IGenericWriter
{
FlushIfNeeded(4);
_buffer[Index++] = (byte)value;
_buffer[Index++] = (byte)(value >> 8);
_buffer[Index++] = (byte)(value >> 16);
_buffer[Index++] = (byte)(value >> 24);
BinaryPrimitives.WriteUInt32LittleEndian(_buffer.AsSpan((int)_index), value);
Index += 4;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -252,8 +232,8 @@ public class BufferWriter : IGenericWriter
{
FlushIfNeeded(2);
_buffer[Index++] = (byte)value;
_buffer[Index++] = (byte)(value >> 8);
BinaryPrimitives.WriteInt16LittleEndian(_buffer.AsSpan((int)_index), value);
Index += 2;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -261,20 +241,16 @@ public class BufferWriter : IGenericWriter
{
FlushIfNeeded(2);
_buffer[Index++] = (byte)value;
_buffer[Index++] = (byte)(value >> 8);
BinaryPrimitives.WriteUInt16LittleEndian(_buffer.AsSpan((int)_index), value);
Index += 2;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void Write(double value)
public void Write(double value)
{
FlushIfNeeded(8);
fixed (byte* pBuffer = _buffer)
{
*(double*)(pBuffer + Index) = value;
}
BinaryPrimitives.WriteDoubleLittleEndian(_buffer.AsSpan((int)_index), value);
Index += 8;
}
@ -283,12 +259,8 @@ public class BufferWriter : IGenericWriter
{
FlushIfNeeded(4);
fixed (byte* pBuffer = _buffer)
{
*(float*)(pBuffer + Index) = value;
}
Index += 4;
BinaryPrimitives.WriteSingleLittleEndian(_buffer.AsSpan((int)_index), value);
Index += 8;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -333,30 +305,16 @@ public class BufferWriter : IGenericWriter
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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
Span<byte> span = stackalloc byte[Math.Min(BufferSize, 256)];
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]);
}
// We don't use spans here since that incurs extra allocations for safety.
Index += _encoding.GetBytes(value, 0, value.Length, _buffer, (int)_index);
}
}