Skip to content

Instantly share code, notes, and snippets.

@JKamsker
Last active April 21, 2022 21:16
Show Gist options
  • Select an option

  • Save JKamsker/80855e63571844ef9ad47f7f872f99a6 to your computer and use it in GitHub Desktop.

Select an option

Save JKamsker/80855e63571844ef9ad47f7f872f99a6 to your computer and use it in GitHub Desktop.
SpanWriter
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnostics.Windows.Configs;
using BenchmarkDotNet.Running;
using Interop_pg;
using Interop_Pg.Extensions;
[MemoryDiagnoser]
[InliningDiagnoser(true, true)]
[TailCallDiagnoser]
[EtwProfiler]
[DisassemblyDiagnoser]
//[ConcurrencyVisualizerProfiler]
//[NativeMemoryProfiler]
//[ThreadingDiagnoser]
public class SpanWriterTests
{
private const int N = 100;
private readonly byte[] data;
public SpanWriterTests()
{
data = new byte[N * 15];
new Random(42).NextBytes(data);
}
[Benchmark]
public void TestSpanWriter()
{
var writer = new SpanWriter(data);
for (int i = 0; i < N; i++)
{
writer.WriteUShort(ushort.MaxValue); //2
writer.WriteBoolean(true); // 1
writer.WriteInt32(123); // 4
writer.WriteInt64(456); // 8
}
}
[Benchmark]
public void TestUnsafeSpanWriter()
{
var writer = new UnsafeSpanWriter(data);
for (int i = 0; i < N; i++)
{
writer.Write(ushort.MaxValue); //2
writer.Write(true); // 1
writer.Write((int)123); // 4
writer.Write((long)456); // 8
}
}
[Benchmark]
public void TestExtensions()
{
var writer = data.AsSpan();
for (int i = 0; i < N; i++)
{
writer.WriteUShort(ushort.MaxValue); //2
writer.WriteBoolean(true); // 1
writer.WriteInt32(123); // 4
writer.WriteInt64(456); // 8
}
}
[Benchmark]
public void TestMemoryMarshalExtensions()
{
var writer = data.AsSpan();
for (int i = 0; i < N; i++)
{
writer.MMWriteUShort(ushort.MaxValue); //2
writer.WriteBoolean(true); // 1
writer.MMWriteInt32(123); // 4
writer.MMWriteInt64(456); // 8
}
}
[Benchmark]
public void TestMemoryStream()
{
var memoryStream = new MemoryStream(data);
memoryStream.Position = 0;
var writer = new BinaryWriter(memoryStream);
for (int i = 0; i < N; i++)
{
writer.Write(ushort.MaxValue); //2
writer.Write(true); // 1
writer.Write((int)123); // 4
writer.Write((long)456); // 8
}
}
}
public class Program
{
public static void Main(string[] args)
{
var summary = BenchmarkRunner.Run(typeof(Program).Assembly);
}
}
public static class MMSpanWriter
{
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(long)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MMWriteInt64(this ref Span<byte> input, long value)
{
MemoryMarshal.Write(SpanWriterExtensions.Advance<long>(ref input), ref value);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(ushort)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MMWriteUShort(this ref Span<byte> input, ushort value)
{
MemoryMarshal.Write(SpanWriterExtensions.Advance<ushort>(ref input), ref value);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(int)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void MMWriteInt32(this ref Span<byte> input, int value)
{
MemoryMarshal.Write(SpanWriterExtensions.Advance<int>(ref input), ref value);
}
}
using System.Runtime.CompilerServices;
using System.Text;
namespace Interop_pg;
public ref struct SpanWriter
{
private Span<byte> _span;
public SpanWriter(Span<byte> span)
{
_span = span;
}
public SpanWriter(ref Span<byte> span)
{
_span = span;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteBoolean(bool value)
{
var original = Advance<byte>();
original[0] = (byte)(value ? 1 : 0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteByte(byte value)
{
var original = Advance<byte>();
original[0] = value;
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(uint)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUInt32(uint value)
{
var original = Advance<uint>();
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
original[2] = (byte)(value >> 16);
original[3] = (byte)(value >> 24);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(float)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void WriteSingle(float value) => WriteUInt32(*(uint*)&value);
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(ushort)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUInt16(ushort value)
{
var original = Advance<ushort>();
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(ulong)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUInt64(ulong value)
{
var original = Advance<ulong>();
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
original[2] = (byte)(value >> 16);
original[3] = (byte)(value >> 24);
original[4] = (byte)(value >> 32);
original[5] = (byte)(value >> 40);
original[6] = (byte)(value >> 48);
original[7] = (byte)(value >> 56);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(long)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteInt64(long value)
{
var original = Advance<long>();
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
original[2] = (byte)(value >> 16);
original[3] = (byte)(value >> 24);
original[4] = (byte)(value >> 32);
original[5] = (byte)(value >> 40);
original[6] = (byte)(value >> 48);
original[7] = (byte)(value >> 56);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(ushort)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUShort(ushort value)
{
var original = Advance<ushort>();
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(int)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteInt32(int value)
{
var original = Advance<int>();
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
original[2] = (byte)(value >> 16);
original[3] = (byte)(value >> 24);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteNullableInt32(int? value)
{
WriteBoolean(value.HasValue);
if (!value.HasValue)
{
return;
}
WriteInt32(value.Value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteString(string value, Encoding encoding = null)
{
encoding ??= Encoding.UTF8;
var byteCount = encoding.GetByteCount(value.AsSpan());
WriteInt32(byteCount);
encoding.GetBytes(value, _span);
_span = _span[byteCount..];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe Span<byte> Advance<T>() where T : unmanaged
{
return Advance(sizeof(T));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe Span<byte> Advance(int size)
{
var original = _span;
var slized = original[size..];
_span = slized;
return original;
}
}
using System;
using System.Buffers.Binary;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace Interop_Pg.Extensions
{
public static class SpanWriterExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteBoolean(this ref Span<byte> input, bool value)
{
var original = Advance<byte>(ref input);
original[0] = (byte)(value ? 1 : 0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteByte(this ref Span<byte> input, byte value)
{
var original = Advance<byte>(ref input);
original[0] = value;
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(uint)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteUInt32(this ref Span<byte> input, uint value)
{
var original = Advance<uint>(ref input);
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
original[2] = (byte)(value >> 16);
original[3] = (byte)(value >> 24);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(float)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void WriteSingle(this ref Span<byte> input, float value) => input.WriteUInt32(*(uint*)&value);
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(ushort)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteUInt16(this ref Span<byte> input, ushort value)
{
var original = Advance<ushort>(ref input);
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(ulong)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteUInt64(this ref Span<byte> input, ulong value)
{
var original = Advance<ulong>(ref input);
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
original[2] = (byte)(value >> 16);
original[3] = (byte)(value >> 24);
original[4] = (byte)(value >> 32);
original[5] = (byte)(value >> 40);
original[6] = (byte)(value >> 48);
original[7] = (byte)(value >> 56);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(long)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteInt64(this ref Span<byte> input, long value)
{
var original = Advance<long>(ref input);
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
original[2] = (byte)(value >> 16);
original[3] = (byte)(value >> 24);
original[4] = (byte)(value >> 32);
original[5] = (byte)(value >> 40);
original[6] = (byte)(value >> 48);
original[7] = (byte)(value >> 56);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(ushort)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteUShort(this ref Span<byte> input, ushort value)
{
var original = Advance<ushort>(ref input);
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
}
/// <summary>
/// Copied from <see cref="BinaryWriter.Write(int)"/>
/// </summary>
/// <param name="input"></param>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteInt32(this ref Span<byte> input, int value)
{
var original = Advance<int>(ref input);
original[0] = (byte)value;
original[1] = (byte)(value >> 8);
original[2] = (byte)(value >> 16);
original[3] = (byte)(value >> 24);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteNullableInt32(this ref Span<byte> input, int? value)
{
input.WriteBoolean(value.HasValue);
if (!value.HasValue)
{
return;
}
input.WriteInt32(value.Value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteString(this ref Span<byte> input, string value, Encoding encoding = null)
{
encoding ??= Encoding.UTF8;
var byteCount = encoding.GetByteCount(value.AsSpan());
input.WriteInt32(byteCount);
encoding.GetBytes(value, input);
input = input[byteCount..];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe Span<byte> Advance<T>(ref Span<byte> input) where T : unmanaged
{
return Advance(ref input, sizeof(T));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Span<byte> Advance(ref Span<byte> input, int size)
{
var original = input;
var slized = input.Slice(size);
input = slized;
return original;
}
}
}
public unsafe ref struct UnsafeSpanWriter
{
private readonly Span<byte> span;
private nint pos;
public readonly int Position => (int)pos;
public UnsafeSpanWriter(Span<byte> buff)
{
span = buff;
pos = default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write<T>(T val) where T : unmanaged
{
Unsafe.WriteUnaligned(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), pos), val);
Advance<T>();
}
private void Advance<T>() where T : unmanaged
{
pos += sizeof(T);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment