--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Buffers;
+using System.Net.Http;
+using Xunit;
+
+namespace Common.Tests.Tests.System.Net.aspnetcore.Http3
+{
+ public class VariableLengthIntegerHelperTests
+ {
+ [Fact]
+ public void TryRead_FromReadOnlySpan_BufferEmpty()
+ {
+ ReadOnlySpan<byte> readOnlySpan = new ReadOnlySpan<byte>();
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(readOnlySpan,
+ out long value, out int bytesRead);
+
+ Assert.False(isSuccess);
+ Assert.Equal(0, value);
+ Assert.Equal(0, bytesRead);
+ }
+
+ [Fact]
+ public void TryRead_FromReadOnlySpan_BufferNotEmpty_InitialOneByteLengthMask()
+ {
+ ReadOnlySpan<byte> readOnlySpan = new ReadOnlySpan<byte>(new byte[]
+ {
+ 1
+ });
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(readOnlySpan,
+ out long value, out int bytesRead);
+
+ Assert.True(isSuccess);
+ Assert.Equal(1, value);
+ Assert.Equal(1, bytesRead);
+ }
+
+ [Fact]
+ public void TryRead_FromReadOnlySpan_BufferNotEmpty_InitialTwoByteLengthMask_Buffer16BigEndian()
+ {
+ ReadOnlySpan<byte> readOnlySpan = new ReadOnlySpan<byte>(new byte[]
+ {
+ 64,
+ 1
+ });
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(readOnlySpan,
+ out long value, out int bytesRead);
+
+ Assert.True(isSuccess);
+ Assert.Equal(1, value);
+ Assert.Equal(2, bytesRead);
+ }
+
+ [Fact]
+ public void TryRead_FromReadOnlySpan_BufferNotEmpty_InitialTwoByteLengthMask_BufferNot16BigEndian()
+ {
+ ReadOnlySpan<byte> readOnlySpan = new ReadOnlySpan<byte>(new byte[]
+ {
+ 64
+ });
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(readOnlySpan,
+ out long value, out int bytesRead);
+
+ Assert.False(isSuccess);
+ Assert.Equal(0, value);
+ Assert.Equal(0, bytesRead);
+ }
+
+ [Fact]
+ public void TryRead_FromReadOnlySpan_BufferNotEmpty_InitialFourByteLengthMask_TryReadUInt32BigEndian()
+ {
+ ReadOnlySpan<byte> readOnlySpan = new ReadOnlySpan<byte>(new byte[]
+ {
+ 128,
+ 0,
+ 0,
+ 2
+ });
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(readOnlySpan,
+ out long value, out int bytesRead);
+
+ Assert.True(isSuccess);
+ Assert.Equal(2, value);
+ Assert.Equal(4, bytesRead);
+ }
+
+ [Fact]
+ public void TryRead_FromReadOnlySpan_BufferNotEmpty_InitialFourByteLengthMask_TryReadNotUInt32BigEndian()
+ {
+ ReadOnlySpan<byte> readOnlySpan = new ReadOnlySpan<byte>(new byte[]
+ {
+ 128
+ });
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(readOnlySpan,
+ out long value, out int bytesRead);
+
+ Assert.False(isSuccess);
+ Assert.Equal(0, value);
+ Assert.Equal(0, bytesRead);
+ }
+
+ [Fact]
+ public void TryRead_FromReadOnlySpan_BufferNotEmpty_InitialEightByteLengthMask_TryReadUInt64BigEndian()
+ {
+ ReadOnlySpan<byte> readOnlySpan = new ReadOnlySpan<byte>(
+ new byte[]
+ {
+ 192, 0, 0, 0,
+ 0, 0, 0, 4
+ });
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(readOnlySpan,
+ out long value, out int bytesRead);
+
+ Assert.True(isSuccess);
+ Assert.Equal(4, value);
+ Assert.Equal(8, bytesRead);
+ }
+
+ [Fact]
+ public void TryRead_FromReadOnlySpan_BufferNotEmpty_InitialEightByteLengthMask_TryReadNotUInt64BigEndian()
+ {
+ ReadOnlySpan<byte> readOnlySpan = new ReadOnlySpan<byte>(new byte[]
+ {
+ 192
+ });
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(readOnlySpan,
+ out long value, out int bytesRead);
+
+ Assert.False(isSuccess);
+ Assert.Equal(0, value);
+ Assert.Equal(0, bytesRead);
+ }
+
+ [Fact]
+ public void TryRead_FromSequenceReader_NotSegmentedSequence()
+ {
+ ReadOnlySequence<byte> readOnlySequence = new ReadOnlySequence<byte>(new byte[]
+ {
+ 1
+ });
+ SequenceReader<byte> sequenceReader = new SequenceReader<byte>(readOnlySequence);
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(ref sequenceReader,
+ out long value);
+
+ Assert.True(isSuccess);
+ Assert.Equal(1, value);
+ Assert.Equal(1, sequenceReader.CurrentSpanIndex);
+ }
+
+ internal class MemorySegment<T> : ReadOnlySequenceSegment<T>
+ {
+ internal MemorySegment(ReadOnlyMemory<T> memory)
+ {
+ Memory = memory;
+ }
+
+ internal MemorySegment<T> Append(ReadOnlyMemory<T> memory)
+ {
+ var segment = new MemorySegment<T>(memory)
+ {
+ RunningIndex = RunningIndex + Memory.Length
+ };
+
+ Next = segment;
+
+ return segment;
+ }
+ }
+
+ [Fact]
+ public void TryRead_FromSequenceReader_InitialTwoByteLengthMask_SegmentedSequence()
+ {
+ MemorySegment<byte> memorySegment1 = new MemorySegment<byte>(new byte[] { 64 });
+ MemorySegment<byte> memorySegment2 = memorySegment1.Append(new byte[] { 1 });
+ ReadOnlySequence<byte> readOnlySequence = new ReadOnlySequence<byte>(
+ memorySegment1, 0, memorySegment2, memorySegment2.Memory.Length);
+ SequenceReader<byte> sequenceReader = new SequenceReader<byte>(readOnlySequence);
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(ref sequenceReader,
+ out long value);
+
+ Assert.True(isSuccess);
+ Assert.Equal(1, value);
+ Assert.Equal(1, sequenceReader.CurrentSpanIndex);
+ }
+
+ [Fact]
+ public void TryRead_FromSequenceReader_InitialFourByteLengthMask_SegmentedSequence()
+ {
+ MemorySegment<byte> memorySegment1 = new MemorySegment<byte>(new byte[] { 192 });
+ MemorySegment<byte> memorySegment2 = memorySegment1.Append(new byte[] { 0, 0, 0, 0, 0, 0, 2 });
+ ReadOnlySequence<byte> readOnlySequence = new ReadOnlySequence<byte>(
+ memorySegment1, 0, memorySegment2, memorySegment2.Memory.Length);
+ SequenceReader<byte> sequenceReader = new SequenceReader<byte>(readOnlySequence);
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(ref sequenceReader,
+ out long value);
+
+ Assert.True(isSuccess);
+ Assert.Equal(2, value);
+ Assert.Equal(7, sequenceReader.CurrentSpanIndex);
+ }
+
+ [Fact]
+ public void TryRead_FromSequenceReader_NotValidSegmentedSequence()
+ {
+ MemorySegment<byte> memorySegment1 = new MemorySegment<byte>(new byte[] { 192 });
+ MemorySegment<byte> memorySegment2 = memorySegment1.Append(new byte[] { 0, 0, 0, 0, 0, 2 });
+ ReadOnlySequence<byte> readOnlySequence = new ReadOnlySequence<byte>(
+ memorySegment1, 0, memorySegment2, memorySegment2.Memory.Length);
+ SequenceReader<byte> sequenceReader = new SequenceReader<byte>(readOnlySequence);
+ bool isSuccess = VariableLengthIntegerHelper.TryRead(ref sequenceReader,
+ out long value);
+
+ Assert.False(isSuccess);
+ Assert.Equal(0, value);
+ }
+
+ [Fact]
+ public void GetInteger_ValidSegmentedSequence()
+ {
+ MemorySegment<byte> memorySegment1 = new MemorySegment<byte>(new byte[] { 192 });
+ MemorySegment<byte> memorySegment2 = memorySegment1.Append(new byte[] { 0, 0, 0, 0, 0, 0, 2 });
+ ReadOnlySequence<byte> readOnlySequence = new ReadOnlySequence<byte>(
+ memorySegment1, 0, memorySegment2, memorySegment2.Memory.Length);
+ long result = VariableLengthIntegerHelper.GetInteger(readOnlySequence,
+ out SequencePosition consumed, out SequencePosition examined);
+
+ Assert.Equal(2, result);
+ Assert.Equal(7, consumed.GetInteger());
+ Assert.Equal(7, examined.GetInteger());
+ }
+
+ [Fact]
+ public void GetInteger_NotValidSegmentedSequence()
+ {
+ MemorySegment<byte> memorySegment1 = new MemorySegment<byte>(new byte[] { 192 });
+ MemorySegment<byte> memorySegment2 = memorySegment1.Append(new byte[] { 0, 0, 0, 0, 0, 2 });
+ ReadOnlySequence<byte> readOnlySequence = new ReadOnlySequence<byte>(
+ memorySegment1, 0, memorySegment2, memorySegment2.Memory.Length);
+ long result = VariableLengthIntegerHelper.GetInteger(readOnlySequence,
+ out SequencePosition consumed, out SequencePosition examined);
+
+ Assert.Equal(-1, result);
+ Assert.Equal(0, consumed.GetInteger());
+ Assert.Equal(6, examined.GetInteger());
+ }
+
+ [Fact]
+ public void TryWrite_BufferEmpty()
+ {
+ Span<byte> span = new Span<byte>();
+ long longToEncode = 1;
+ bool isSuccess = VariableLengthIntegerHelper.TryWrite(span,
+ longToEncode, out int bytesWritten);
+
+ Assert.False(isSuccess);
+ Assert.Equal(0, bytesWritten);
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(2)]
+ [InlineData(63)]
+ public void TryWrite_BufferNotEmpty_OneByteLimit(long longToEncode)
+ {
+ Span<byte> span = new Span<byte>(new byte[1]);
+ bool isSuccess = VariableLengthIntegerHelper.TryWrite(span,
+ longToEncode, out int bytesWritten);
+
+ Assert.True(isSuccess);
+ Assert.Equal(1, bytesWritten);
+ Assert.Equal(longToEncode, span[0]);
+ }
+
+ [Theory]
+ [InlineData(64, new byte[] { 64, 64 })]
+ [InlineData(66, new byte[] { 64, 66 })]
+ [InlineData(16383, new byte[] { 127, 255})]
+ public void TryWrite_BufferNotEmpty_TwoByteLimit(long longToEncode,
+ byte[] expected)
+ {
+ Span<byte> span = new Span<byte>(new byte[2]);
+ bool isSuccess = VariableLengthIntegerHelper.TryWrite(span,
+ longToEncode, out int bytesWritten);
+
+ Assert.True(isSuccess);
+ Assert.Equal(2, bytesWritten);
+ Assert.Equal(expected, span.ToArray());
+ }
+
+ [Fact]
+ public void TryWrite_BufferNotSizedCorrectly_TwoByteLimit()
+ {
+ long longToEncode = 64;
+ Span<byte> span = new Span<byte>(new byte[1]);
+ bool isSuccess = VariableLengthIntegerHelper.TryWrite(span,
+ longToEncode, out int bytesWritten);
+
+ Assert.False(isSuccess);
+ Assert.Equal(0, bytesWritten);
+ }
+
+ [Theory]
+ [InlineData(16384, new byte[] {128, 0, 64, 0})]
+ [InlineData(16386, new byte[] { 128, 0, 64, 2 })]
+ [InlineData(1073741823, new byte[] { 191, 255, 255, 255 })]
+ public void TryWrite_BufferNotEmpty_FourByteLimit(long longToEncode,
+ byte[] expected)
+ {
+ Span<byte> span = new Span<byte>(new byte[4]);
+ bool isSuccess = VariableLengthIntegerHelper.TryWrite(span,
+ longToEncode, out int bytesWritten);
+
+ Assert.True(isSuccess);
+ Assert.Equal(4, bytesWritten);
+ Assert.Equal(expected, span.ToArray());
+ }
+
+ [Fact]
+ public void TryWrite_BufferNotSizedCorrectly_FourByteLimit()
+ {
+ long longToEncode = 16384;
+ Span<byte> span = new Span<byte>(new byte[1]);
+ bool isSuccess = VariableLengthIntegerHelper.TryWrite(span,
+ longToEncode, out int bytesWritten);
+
+ Assert.False(isSuccess);
+ Assert.Equal(0, bytesWritten);
+ }
+
+ [Theory]
+ [InlineData(1073741824, new byte[] { 192, 0, 0, 0, 64, 0, 0, 0 })]
+ [InlineData(1073741826, new byte[] { 192, 0, 0, 0, 64, 0, 0, 2 })]
+ [InlineData(4611686018427387903, new byte[] { 255, 255, 255, 255, 255, 255, 255, 255 })]
+ public void TryWrite_BufferNotEmpty_EightByteLimit(long longToEncode,
+ byte[] expected)
+ {
+ Span<byte> span = new Span<byte>(new byte[8]);
+ bool isSuccess = VariableLengthIntegerHelper.TryWrite(span,
+ longToEncode, out int bytesWritten);
+
+ Assert.True(isSuccess);
+ Assert.Equal(8, bytesWritten);
+ Assert.Equal(expected, span.ToArray());
+ }
+
+ [Fact]
+ public void TryWrite_BufferNotSizedCorrectly_EightByteLimit()
+ {
+ long longToEncode = 1073741824;
+ Span<byte> span = new Span<byte>(new byte[1]);
+ bool isSuccess = VariableLengthIntegerHelper.TryWrite(span,
+ longToEncode, out int bytesWritten);
+
+ Assert.False(isSuccess);
+ Assert.Equal(0, bytesWritten);
+ }
+
+ [Theory]
+ [InlineData(1, 1)]
+ [InlineData(64, 2)]
+ [InlineData(16384, 4)]
+ [InlineData(1073741824, 8)]
+ public void GetByteCountTest(long longToEncode, int expectedLimit)
+ {
+ int result = VariableLengthIntegerHelper.GetByteCount(longToEncode);
+
+ Assert.Equal(expectedLimit, result);
+ }
+ }
+}