Last active
April 21, 2022 21:16
-
-
Save JKamsker/80855e63571844ef9ad47f7f872f99a6 to your computer and use it in GitHub Desktop.
SpanWriter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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