Я пытаюсь реализовать сетевую библиотеку, чтобы завершить свою работу с нуля Astron , так как у меня есть несколько дней, и у меня есть несколько вопросов.Чтобы правильно обрабатывать Nagle, у меня должна быть логика разбора кадра.Поэтому, делая это с использованием нового конвейерного API, я заметил, что вывод был предоставлен в виде последовательности только для чтения, поэтому мне пришлось создать собственный SequenceReader, но у меня возникли некоторые проблемы при его тестировании.Я думаю, что я неправильно понял новый Span<T>
и Memory<T>
API: /
- Уместно ли имитировать сегмент последовательности?
Чтобы проверить какДля всех возможных вариантов поведения мне пришлось реализовать абстрактный класс ReadOnlySequenceSegment<T>
, который я использовал с помощью библиотеки Moq
, чтобы узнать, когда поступает следующий сегмент, вот он:
public class SequenceSegment : ReadOnlySequenceSegment<byte>
{
private readonly Mock<SequenceSegment> _mock;
private ReadOnlySequenceSegment<byte> _next;
public new ReadOnlySequenceSegment<byte> Next
{
get
{
var mockNext = _mock.Object.Next; // simulate get from the mock
return _next;
}
protected set => _next = value;
}
public SequenceSegment(ReadOnlyMemory<byte> memory)
{
Memory = memory;
_mock = new Mock<SequenceSegment>(memory);
}
public SequenceSegment Add(ReadOnlyMemory<byte> mem)
{
var segment = new SequenceSegment(mem)
{
RunningIndex = RunningIndex + Memory.Length
};
Next = segment;
return segment;
}
public void VerifyGetNext() => _mock.VerifyGet(ss => ss.Next);
}
Как выможно увидеть, что мне пришлось переопределить свойство Next с помощью ключевого слова new
, что является плохой практикой, но я предполагаю, что во время тестирования это нормально?
Вот тест, который не проходит:
[Fact]
public void TryRead_ShouldReturnTrue_OnSegmentSplitted()
{
var buffer = _createBuffer(); // int + int = 8bytes buffer
var firstSegment = new SequenceSegment(buffer.Slice(0, 3));
var secondSegment = firstSegment.Add(buffer.Slice(3, 5));
var input = new ReadOnlySequence<byte>(
firstSegment, 0,
secondSegment, 4);
var reader = new SequenceReader(input);
reader.TryRead(BinaryPrimitives.ReadInt32LittleEndian, out var firstValue); // throw here
reader.TryRead(BinaryPrimitives.ReadInt32LittleEndian, out var secondValue);
Assert.Equal(_firstValue, firstValue);
Assert.Equal(_secondValue, secondValue);
firstSegment.VerifyGetNext();
}
Тестовый вывод:
Message: System.ArgumentOutOfRangeException : Specified argument was out of the range of valid values.
Parameter name: start
Я прокомментировал строку, которая выдает исключение в тесте, поэтому я предполагаю, что моя логика последовательности в порядке?Давайте посмотрим код моего SequenceReader со строкой, которая выдает комментарий:
public class SequenceReader
{
private const int _maxStackalloc = 128;
protected ReadOnlySequence<byte> _input;
public SequenceReader(ReadOnlySequence<byte> input) => _input = input;
public delegate T ReadDelegate<out T>(ReadOnlySpan<byte> src);
/// <summary>
/// Try to read a <see cref="T"/> with the <see cref="ReadDelegate{T}"/> specified as an arg.
/// The <see cref="SequenceReader"/> then advance the current position according to the size of <see cref="T"/>.
/// <see cref="T"/> must be a struct :
/// <see cref="byte"/>, <see cref="sbyte"/>, <see cref="bool"/>, <see cref="short"/>,
/// <see cref="ushort"/>, <see cref="int"/>, <see cref="uint"/>, <see cref="long"/>,
/// <see cref="ulong"/>, <see cref="float"/>, <see cref="double"/>, <see cref="decimal"/>,
/// </summary>
/// <typeparam name="T">The type to read.</typeparam>
/// <param name="read">The delegate to read the <see cref="T"/>. Must be a method from <see cref="BinaryPrimitives"/></param>
/// <param name="result">The result returned.</param>
/// <returns>Returns true if the read was successful, else returns false.</returns>
public unsafe bool TryRead<T>(ReadDelegate<T> read, out T result) where T : unmanaged
{
result = default;
var size = sizeof(T);
if (size > _maxStackalloc) return false;
if (size > _input.Length) return false;
if (_input.First.Length >= size)
result = read(_input.First.Span);
else
{
Span<byte> local = stackalloc byte[size];
_input.Slice(size).CopyTo(local); // throws at the slice
result = read(local);
}
_input = _input.Slice(size);
return true;
}
}
Я уже пытался изменить строку на _input.Slice(0, size)
, но ничего не изменилось, также не в этом успешном тесте:
[Fact]
public void TryRead_ShouldReturnTrue_OnSegmentComplete()
{
var input = new ReadOnlySequence<byte>(_createBuffer());
var reader = new SequenceReader(input);
reader.TryRead(BinaryPrimitives.ReadInt32LittleEndian, out var firstValue);
reader.TryRead(BinaryPrimitives.ReadInt32LittleEndian, out var secondValue);
Assert.Equal(_firstValue, firstValue);
Assert.Equal(_secondValue, secondValue);
}
Мне интересно, что я делаю неправильно, пожалуйста, если у вас есть идеи, было бы здорово, если бы вы могли мне помочь!