From 3e3e5c28ec44d02926ace0174546745484540df8 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 5 Dec 2024 19:10:42 +0200 Subject: [PATCH] Added helpers for one-shot encoding and decoding --- .../Buffers/Binary/Leb128Tests.cs | 14 +++--- .../Buffers/SpanReaderWriterTests.cs | 23 +++++++++ src/DotNext/Buffers/Binary/Leb128.cs | 47 +++++++++++++++++++ 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/DotNext.Tests/Buffers/Binary/Leb128Tests.cs b/src/DotNext.Tests/Buffers/Binary/Leb128Tests.cs index 3efe04055..8433f2395 100644 --- a/src/DotNext.Tests/Buffers/Binary/Leb128Tests.cs +++ b/src/DotNext.Tests/Buffers/Binary/Leb128Tests.cs @@ -8,16 +8,13 @@ private static void EncodeDecode(ReadOnlySpan values) where T : struct, IBinaryInteger { Span buffer = stackalloc byte[Leb128.MaxSizeInBytes]; - var writer = new SpanWriter(buffer); - var reader = new SpanReader(buffer); foreach (var expected in values) { - writer.Reset(); - reader.Reset(); - - True(writer.WriteLeb128(expected) > 0); - Equal(expected, reader.ReadLeb128()); + True(Leb128.TryGetBytes(expected, buffer, out var bytesWritten)); + True(Leb128.TryParse(buffer, out var actual, out var bytesConsumed)); + Equal(bytesWritten, bytesConsumed); + Equal(expected, actual); } } @@ -27,6 +24,9 @@ private static void EncodeDecode(ReadOnlySpan values) [Fact] public static void EncodeDecodeInt64() => EncodeDecode([0L, long.MaxValue, long.MinValue, 0x80L, -1L]); + [Fact] + public static void EncodeDecodeInt128() => EncodeDecode([0, Int128.MaxValue, Int128.MinValue, 0x80, Int128.NegativeOne]); + [Fact] public static void EncodeDecodeUInt32() => EncodeDecode([uint.MinValue, uint.MaxValue, 0x80U]); } \ No newline at end of file diff --git a/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs b/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs index 47c2ae35b..c246053ef 100644 --- a/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs +++ b/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs @@ -452,4 +452,27 @@ public static void WriteLengthPrefixedBytes(LengthFormat format) using var actual = reader.ReadBlock(format); Equal(expected, actual.Span); } + + private static void EncodeDecodeLeb128(ReadOnlySpan values) + where T : struct, IBinaryInteger + { + Span buffer = stackalloc byte[Leb128.MaxSizeInBytes]; + var writer = new SpanWriter(buffer); + var reader = new SpanReader(buffer); + + foreach (var expected in values) + { + writer.Reset(); + reader.Reset(); + + True(writer.WriteLeb128(expected) > 0); + Equal(expected, reader.ReadLeb128()); + } + } + + [Fact] + public static void EncodeDecodeInt32() => EncodeDecodeLeb128([0, int.MaxValue, int.MinValue, 0x80, -1]); + + [Fact] + public static void EncodeDecodeInt64() => EncodeDecodeLeb128([0L, long.MaxValue, long.MinValue, 0x80L, -1L]); } \ No newline at end of file diff --git a/src/DotNext/Buffers/Binary/Leb128.cs b/src/DotNext/Buffers/Binary/Leb128.cs index ff81fd9b6..8afd60b49 100644 --- a/src/DotNext/Buffers/Binary/Leb128.cs +++ b/src/DotNext/Buffers/Binary/Leb128.cs @@ -95,6 +95,53 @@ public T Value /// public readonly Enumerator GetEnumerator() => new(value); + /// + /// Tries to encode the value by using LEB128 binary format. + /// + /// The value to encode. + /// The output buffer. + /// The number of bytes written. + /// if has enough space to save the encoded value; otherwise, . + public static bool TryGetBytes(T value, Span buffer, out int bytesWritten) + { + bytesWritten = 0; + var index = 0; + foreach (var octet in new Leb128 { Value = value }) + { + if ((uint)index >= (uint)buffer.Length) + return false; + + buffer[index++] = octet; + } + + bytesWritten = index; + return true; + } + + /// + /// Decodes LEB128-encoded integer. + /// + /// The input buffer containing LEB128 octets. + /// The decoded value. + /// The number of bytes consumed from . + /// if operation is successful; otherwise, . + public static bool TryParse(ReadOnlySpan buffer, out T result, out int bytesConsumed) + { + bytesConsumed = 0; + var decoder = new Leb128(); + var successful = false; + + foreach (var octet in buffer) + { + bytesConsumed += 1; + if (successful = !decoder.Append(octet)) + break; + } + + result = decoder.Value; + return successful; + } + /// /// Represents an enumerator that produces 7-bit encoded integer as a sequence of octets. ///