From 4155667dc483a9fae38b6161b0a387e5574a8ad0 Mon Sep 17 00:00:00 2001 From: deniaa Date: Wed, 1 Jun 2022 13:25:14 +0500 Subject: [PATCH] Add interfaces and implementatoins with ReadonlySpan to IBinaryReader and IBinaryWriter --- .../BinaryReadersWriters_Tests.cs | 16 ++++++++-- Vostok.Commons.Binary/BinaryBufferReader.cs | 28 +++++++++++++++++ Vostok.Commons.Binary/BinaryBufferWriter.cs | 30 +++++++++++++++++++ Vostok.Commons.Binary/BinaryStreamReader.cs | 12 ++++++++ Vostok.Commons.Binary/BinaryStreamWriter.cs | 13 ++++++++ Vostok.Commons.Binary/IBinaryReader.cs | 15 ++++++++++ Vostok.Commons.Binary/IBinaryWriter.cs | 14 +++++++++ 7 files changed, 126 insertions(+), 2 deletions(-) diff --git a/Vostok.Commons.Binary.Tests/BinaryReadersWriters_Tests.cs b/Vostok.Commons.Binary.Tests/BinaryReadersWriters_Tests.cs index 43d466f..633ba4c 100644 --- a/Vostok.Commons.Binary.Tests/BinaryReadersWriters_Tests.cs +++ b/Vostok.Commons.Binary.Tests/BinaryReadersWriters_Tests.cs @@ -225,6 +225,18 @@ public void Should_correctly_write_and_read_byte_array_values(params byte[] valu Test(value, (item, writer) => writer.WriteWithoutLength(item), reader => reader.ReadByteArray(value.Length)); } + +#if NET6_0_OR_GREATER + [TestCase((byte)0xFF)] + [TestCase((byte)0xFF, (byte)0xAB)] + [TestCase((byte)0xC0, (byte)0xFF, (byte)0xEE, (byte)0xBA, (byte)0xBE)] + public void Should_correctly_write_and_read_bytes_span_values(params byte[] value) + { + Test(value.AsSpan(), (item, writer) => writer.WriteWithLength(item), reader => reader.ReadBytesSpan()); + Test(value.AsSpan(), (item, writer) => writer.WriteWithoutLength(item), reader => reader.ReadBytesSpan(value.Length)); + } +#endif + [Test] public void Should_correctly_write_and_read_timespan_values() { @@ -312,7 +324,7 @@ public void Should_correctly_write_and_read_nullable_classes() reader => reader.ReadNullable(r => r.ReadString())); } - #region Helpers +#region Helpers private static void Test(T item, Action write, Func read) { @@ -524,6 +536,6 @@ public override void Flush() => throw new NotSupportedException(); } - #endregion +#endregion } } \ No newline at end of file diff --git a/Vostok.Commons.Binary/BinaryBufferReader.cs b/Vostok.Commons.Binary/BinaryBufferReader.cs index 10ce500..9f81f51 100644 --- a/Vostok.Commons.Binary/BinaryBufferReader.cs +++ b/Vostok.Commons.Binary/BinaryBufferReader.cs @@ -306,5 +306,33 @@ private void EnsureSufficientSizeRemaining(int size) if (size > BytesRemaining) throw new IndexOutOfRangeException($"Requested to read {size} bytes from buffer, but it only has {BytesRemaining} bytes remaining."); } + +#if NET6_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan ReadBytesSpan() + { + var size = ReadInt32(); + + EnsureSufficientSizeRemaining(size); + + var result = new ReadOnlySpan(Buffer, (int)Position, size); + + Position += size; + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan ReadBytesSpan(int size) + { + EnsureSufficientSizeRemaining(size); + + var result = new ReadOnlySpan(Buffer, (int)Position, size); + + Position += size; + + return result; + } +#endif } } \ No newline at end of file diff --git a/Vostok.Commons.Binary/BinaryBufferWriter.cs b/Vostok.Commons.Binary/BinaryBufferWriter.cs index e2b57b3..4be1b0d 100644 --- a/Vostok.Commons.Binary/BinaryBufferWriter.cs +++ b/Vostok.Commons.Binary/BinaryBufferWriter.cs @@ -347,6 +347,36 @@ public void WriteWithoutLength(byte[] value, int offset, int length) IncreaseLengthIfNeeded(); } +#if NET6_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteWithLength(ReadOnlySpan value) + { + var length = value.Length; + EnsureCapacity(length + sizeof(int)); + + Write(length); + + value.CopyTo(new Span(Buffer, position, length)); + + position += length; + + IncreaseLengthIfNeeded(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteWithoutLength(ReadOnlySpan value) + { + var length = value.Length; + EnsureCapacity(length); + + value.CopyTo(new Span(Buffer, position, length)); + + position += length; + + IncreaseLengthIfNeeded(); + } +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset(int neededCapacity = 0) { diff --git a/Vostok.Commons.Binary/BinaryStreamReader.cs b/Vostok.Commons.Binary/BinaryStreamReader.cs index 429dcb7..489911d 100644 --- a/Vostok.Commons.Binary/BinaryStreamReader.cs +++ b/Vostok.Commons.Binary/BinaryStreamReader.cs @@ -164,6 +164,18 @@ public byte[] ReadByteArray(int size) return result; } +#if NET6_0_OR_GREATER + public ReadOnlySpan ReadBytesSpan() + { + return ReadByteArray().AsSpan(); + } + + public ReadOnlySpan ReadBytesSpan(int size) + { + return ReadByteArray(size).AsSpan(); + } +#endif + private void LoadIntoBufferExactly(int size) { ReadFromStreamExactly(buffer.Buffer, 0, size); diff --git a/Vostok.Commons.Binary/BinaryStreamWriter.cs b/Vostok.Commons.Binary/BinaryStreamWriter.cs index c08872f..db36a9f 100644 --- a/Vostok.Commons.Binary/BinaryStreamWriter.cs +++ b/Vostok.Commons.Binary/BinaryStreamWriter.cs @@ -194,6 +194,19 @@ public void WriteWithoutLength(byte[] value, int offset, int length) stream.Write(value, offset, length); } +#if NET6_0_OR_GREATER + public void WriteWithLength(ReadOnlySpan value) + { + Write(value.Length); + WriteWithoutLength(value); + } + + public void WriteWithoutLength(ReadOnlySpan value) + { + stream.Write(value); + } +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Flush() { diff --git a/Vostok.Commons.Binary/IBinaryReader.cs b/Vostok.Commons.Binary/IBinaryReader.cs index 9de1ecf..d40655a 100644 --- a/Vostok.Commons.Binary/IBinaryReader.cs +++ b/Vostok.Commons.Binary/IBinaryReader.cs @@ -96,5 +96,20 @@ internal interface IBinaryReader /// [NotNull] byte[] ReadByteArray(int size); + +#if NET6_0_OR_GREATER + /// + /// Reads a span of bytes. + /// Assumes that the value itself is prepended by its Int32 length. + /// The resulting ReadOnlySpan may hold a reference to the source buffer if it exists. Be careful not to use this span after buffer and this reader released. Underlying buffer may be returned to pool and used under another reader. Or you can keep a huge buffer while this span is alive too. + /// + ReadOnlySpan ReadBytesSpan(); + + /// + /// Reads a span of bytes of given . + /// The resulting ReadOnlySpan may hold a reference to the source buffer if it exists. Be careful not to hold this span forever to avoid holding the source buffer. + /// + ReadOnlySpan ReadBytesSpan(int size); +#endif } } \ No newline at end of file diff --git a/Vostok.Commons.Binary/IBinaryWriter.cs b/Vostok.Commons.Binary/IBinaryWriter.cs index d64fb0b..bbf4cc4 100644 --- a/Vostok.Commons.Binary/IBinaryWriter.cs +++ b/Vostok.Commons.Binary/IBinaryWriter.cs @@ -105,5 +105,19 @@ internal interface IBinaryWriter /// The value itself is not automatically prepended with length (readers of binary result must know the length from external sources). /// void WriteWithoutLength([NotNull] byte[] value, int offset, int length); + +#if NET6_0_OR_GREATER + /// + /// Writes given span of bytes as bytes array in its entirety. + /// The value itself is automatically prepended with Int32 length (see ). + /// + void WriteWithLength(ReadOnlySpan value); + + /// + /// Writes given span of bytes as bytes array in its entirety. + /// The value itself is not automatically prepended with length (readers of binary result must know the length from external sources). + /// + void WriteWithoutLength(ReadOnlySpan value); +#endif } } \ No newline at end of file