Use Vector<byte>.ctor workaround

This commit is contained in:
Ben Adams 2016-10-15 20:05:50 +01:00
parent 972d978d11
commit 97d4406614
5 changed files with 97 additions and 111 deletions

View File

@ -7,7 +7,6 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Numerics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -24,6 +23,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{
public abstract partial class Frame : IFrameControl
{
// byte consts don't have a data type annotation so we pre-cast them
private const byte ByteCR = (byte)'\r';
private const byte ByteLF = (byte)'\n';
private const byte ByteColon = (byte)':';
private const byte ByteSpace = (byte)' ';
private const byte ByteTab = (byte)'\t';
private const byte ByteQuestionMark = (byte)'?';
private const byte BytePercentage = (byte)'%';
private static readonly ArraySegment<byte> _endChunkedResponseBytes = CreateAsciiByteArraySegment("0\r\n\r\n");
private static readonly ArraySegment<byte> _continueBytes = CreateAsciiByteArraySegment("HTTP/1.1 100 Continue\r\n\r\n");
@ -35,14 +43,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
private static readonly byte[] _bytesEndHeaders = Encoding.ASCII.GetBytes("\r\n\r\n");
private static readonly byte[] _bytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel");
private static Vector<byte> _vectorCRs = new Vector<byte>((byte)'\r');
private static Vector<byte> _vectorLFs = new Vector<byte>((byte)'\n');
private static Vector<byte> _vectorColons = new Vector<byte>((byte)':');
private static Vector<byte> _vectorSpaces = new Vector<byte>((byte)' ');
private static Vector<byte> _vectorTabs = new Vector<byte>((byte)'\t');
private static Vector<byte> _vectorQuestionMarks = new Vector<byte>((byte)'?');
private static Vector<byte> _vectorPercentages = new Vector<byte>((byte)'%');
private readonly object _onStartingSync = new Object();
private readonly object _onCompletedSync = new Object();
@ -952,7 +952,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
_requestProcessingStatus = RequestProcessingStatus.RequestStarted;
int bytesScanned;
if (end.Seek(ref _vectorLFs, out bytesScanned, ServerOptions.Limits.MaxRequestLineSize) == -1)
if (end.Seek(ByteLF, out bytesScanned, ServerOptions.Limits.MaxRequestLineSize) == -1)
{
if (bytesScanned >= ServerOptions.Limits.MaxRequestLineSize)
{
@ -969,7 +969,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
var begin = scan;
if (!begin.GetKnownMethod(out method))
{
if (scan.Seek(ref _vectorSpaces, ref end) == -1)
if (scan.Seek(ByteSpace, ref end) == -1)
{
RejectRequest(RequestRejectionReason.InvalidRequestLine,
Log.IsEnabled(LogLevel.Information) ? start.GetAsciiStringEscaped(end, MaxInvalidRequestLineChars) : string.Empty);
@ -1002,16 +1002,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
scan.Take();
begin = scan;
var needDecode = false;
var chFound = scan.Seek(ref _vectorSpaces, ref _vectorQuestionMarks, ref _vectorPercentages, ref end);
var chFound = scan.Seek(ByteSpace, ByteQuestionMark, BytePercentage, ref end);
if (chFound == -1)
{
RejectRequest(RequestRejectionReason.InvalidRequestLine,
Log.IsEnabled(LogLevel.Information) ? start.GetAsciiStringEscaped(end, MaxInvalidRequestLineChars) : string.Empty);
}
else if (chFound == '%')
else if (chFound == BytePercentage)
{
needDecode = true;
chFound = scan.Seek(ref _vectorSpaces, ref _vectorQuestionMarks, ref end);
chFound = scan.Seek(ByteSpace, ByteQuestionMark, ref end);
if (chFound == -1)
{
RejectRequest(RequestRejectionReason.InvalidRequestLine,
@ -1023,10 +1023,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
var pathEnd = scan;
var queryString = "";
if (chFound == '?')
if (chFound == ByteQuestionMark)
{
begin = scan;
if (scan.Seek(ref _vectorSpaces, ref end) == -1)
if (scan.Seek(ByteSpace, ref end) == -1)
{
RejectRequest(RequestRejectionReason.InvalidRequestLine,
Log.IsEnabled(LogLevel.Information) ? start.GetAsciiStringEscaped(end, MaxInvalidRequestLineChars) : string.Empty);
@ -1036,7 +1036,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
var queryEnd = scan;
if (pathBegin.Peek() == ' ')
if (pathBegin.Peek() == ByteSpace)
{
RejectRequest(RequestRejectionReason.InvalidRequestLine,
Log.IsEnabled(LogLevel.Information) ? start.GetAsciiStringEscaped(end, MaxInvalidRequestLineChars) : string.Empty);
@ -1044,7 +1044,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
scan.Take();
begin = scan;
if (scan.Seek(ref _vectorCRs, ref end) == -1)
if (scan.Seek(ByteCR, ref end) == -1)
{
RejectRequest(RequestRejectionReason.InvalidRequestLine,
Log.IsEnabled(LogLevel.Information) ? start.GetAsciiStringEscaped(end, MaxInvalidRequestLineChars) : string.Empty);
@ -1067,7 +1067,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
}
scan.Take(); // consume CR
if (scan.Take() != '\n')
if (scan.Take() != ByteLF)
{
RejectRequest(RequestRejectionReason.InvalidRequestLine,
Log.IsEnabled(LogLevel.Information) ? start.GetAsciiStringEscaped(end, MaxInvalidRequestLineChars) : string.Empty);
@ -1208,7 +1208,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{
return false;
}
else if (ch == '\r')
else if (ch == ByteCR)
{
// Check for final CRLF.
end.Take();
@ -1218,7 +1218,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{
return false;
}
else if (ch == '\n')
else if (ch == ByteLF)
{
ConnectionControl.CancelTimeout();
consumed = end;
@ -1228,7 +1228,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
// Headers don't end in CRLF line.
RejectRequest(RequestRejectionReason.HeadersCorruptedInvalidHeaderSequence);
}
else if (ch == ' ' || ch == '\t')
else if (ch == ByteSpace || ch == ByteTab)
{
RejectRequest(RequestRejectionReason.HeaderLineMustNotStartWithWhitespace);
}
@ -1241,7 +1241,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
}
int bytesScanned;
if (end.Seek(ref _vectorLFs, out bytesScanned, _remainingRequestHeadersBytesAllowed) == -1)
if (end.Seek(ByteLF, out bytesScanned, _remainingRequestHeadersBytesAllowed) == -1)
{
if (bytesScanned >= _remainingRequestHeadersBytesAllowed)
{
@ -1254,7 +1254,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
}
var beginName = scan;
if (scan.Seek(ref _vectorColons, ref end) == -1)
if (scan.Seek(ByteColon, ref end) == -1)
{
RejectRequest(RequestRejectionReason.NoColonCharacterFoundInHeaderLine);
}
@ -1263,7 +1263,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
scan.Take();
var validateName = beginName;
if (validateName.Seek(ref _vectorSpaces, ref _vectorTabs, ref endName) != -1)
if (validateName.Seek(ByteSpace, ByteTab, ref endName) != -1)
{
RejectRequest(RequestRejectionReason.WhitespaceIsNotAllowedInHeaderName);
}
@ -1271,14 +1271,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
var beginValue = scan;
ch = scan.Take();
while (ch == ' ' || ch == '\t')
while (ch == ByteSpace || ch == ByteTab)
{
beginValue = scan;
ch = scan.Take();
}
scan = beginValue;
if (scan.Seek(ref _vectorCRs, ref end) == -1)
if (scan.Seek(ByteCR, ref end) == -1)
{
RejectRequest(RequestRejectionReason.MissingCRInHeaderLine);
}
@ -1287,7 +1287,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
ch = scan.Take(); // expecting '\n'
end = scan;
if (ch != '\n')
if (ch != ByteLF)
{
RejectRequest(RequestRejectionReason.HeaderValueMustNotContainCR);
}
@ -1297,7 +1297,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{
return false;
}
else if (next == ' ' || next == '\t')
else if (next == ByteSpace || next == ByteTab)
{
// From https://tools.ietf.org/html/rfc7230#section-3.2.4:
//
@ -1330,15 +1330,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
var endValue = scan;
do
{
ws.Seek(ref _vectorSpaces, ref _vectorTabs, ref _vectorCRs);
ws.Seek(ByteSpace, ByteTab, ByteCR);
endValue = ws;
ch = ws.Take();
while (ch == ' ' || ch == '\t')
while (ch == ByteSpace || ch == ByteTab)
{
ch = ws.Take();
}
} while (ch != '\r');
} while (ch != ByteCR);
var name = beginName.GetArraySegment(endName);
var value = beginValue.GetAsciiString(endValue);

View File

@ -3,7 +3,6 @@
using System;
using System.IO;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
@ -390,9 +389,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
/// </summary>
private class ForChunkedEncoding : MessageBody
{
// This causes an InvalidProgramException if made static
// https://github.com/dotnet/corefx/issues/8825
private Vector<byte> _vectorCRs = new Vector<byte>((byte)'\r');
// byte consts don't have a data type annotation so we pre-cast it
private const byte ByteCR = (byte)'\r';
private readonly SocketInput _input;
private readonly FrameRequestHeaders _requestHeaders;
@ -613,7 +611,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
// Just drain the data
do
{
if (scan.Seek(ref _vectorCRs) == -1)
if (scan.Seek(ByteCR) == -1)
{
// End marker not found yet
consumed = scan;

View File

@ -236,14 +236,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
}
}
public int Seek(ref Vector<byte> byte0Vector)
public int Seek(byte byte0)
{
int bytesScanned;
return Seek(ref byte0Vector, out bytesScanned);
return Seek(byte0, out bytesScanned);
}
public unsafe int Seek(
ref Vector<byte> byte0Vector,
byte byte0,
out int bytesScanned,
int limit = int.MaxValue)
{
@ -259,7 +259,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
var wasLastBlock = block.Next == null;
var following = block.End - index;
byte[] array;
var byte0 = byte0Vector[0];
var byte0Vector = GetVector(byte0);
while (true)
{
@ -352,7 +352,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
}
public unsafe int Seek(
ref Vector<byte> byte0Vector,
byte byte0,
ref MemoryPoolIterator limit)
{
if (IsDefault)
@ -365,7 +365,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
var wasLastBlock = block.Next == null;
var following = block.End - index;
byte[] array;
var byte0 = byte0Vector[0];
var byte0Vector = GetVector(byte0);
while (true)
{
@ -453,15 +453,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
}
}
public int Seek(ref Vector<byte> byte0Vector, ref Vector<byte> byte1Vector)
public int Seek(byte byte0, byte byte1)
{
var limit = new MemoryPoolIterator();
return Seek(ref byte0Vector, ref byte1Vector, ref limit);
return Seek(byte0, byte1, ref limit);
}
public unsafe int Seek(
ref Vector<byte> byte0Vector,
ref Vector<byte> byte1Vector,
byte byte0,
byte byte1,
ref MemoryPoolIterator limit)
{
if (IsDefault)
@ -476,8 +476,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
byte[] array;
int byte0Index = int.MaxValue;
int byte1Index = int.MaxValue;
var byte0 = byte0Vector[0];
var byte1 = byte1Vector[0];
var byte0Vector = GetVector(byte0);
var byte1Vector = GetVector(byte1);
while (true)
{
@ -595,16 +595,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
}
}
public int Seek(ref Vector<byte> byte0Vector, ref Vector<byte> byte1Vector, ref Vector<byte> byte2Vector)
public int Seek(byte byte0, byte byte1, byte byte2)
{
var limit = new MemoryPoolIterator();
return Seek(ref byte0Vector, ref byte1Vector, ref byte2Vector, ref limit);
return Seek(byte0, byte1, byte2, ref limit);
}
public unsafe int Seek(
ref Vector<byte> byte0Vector,
ref Vector<byte> byte1Vector,
ref Vector<byte> byte2Vector,
byte byte0,
byte byte1,
byte byte2,
ref MemoryPoolIterator limit)
{
if (IsDefault)
@ -620,9 +620,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
int byte0Index = int.MaxValue;
int byte1Index = int.MaxValue;
int byte2Index = int.MaxValue;
var byte0 = byte0Vector[0];
var byte1 = byte1Vector[0];
var byte2 = byte2Vector[0];
var byte0Vector = GetVector(byte0);
var byte1Vector = GetVector(byte1);
var byte2Vector = GetVector(byte2);
while (true)
{
@ -1035,6 +1035,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
_index = blockIndex;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector<byte> GetVector(byte vectorByte)
{
// Vector<byte> .ctor doesn't become an intrinsic due to detection issue
// However this does cause it to become an intrinsic (with additional multiply and reg->reg copy)
// https://github.com/dotnet/coreclr/issues/7459#issuecomment-253965670
return Vector.AsVectorByte(new Vector<ulong>(vectorByte * 0x0101010101010101ul));
}
private static ulong PowerOfTwoToHighByte()
{
return BitConverter.IsLittleEndian ? 0x20406080A0C0E0ul : 0xE0C0A080604020ul;

View File

@ -19,35 +19,31 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
block.Array[block.End++] = ch;
}
var vectorMaxValues = new Vector<byte>(byte.MaxValue);
var iterator = block.GetIterator();
foreach (var ch in Enumerable.Range(0, 256).Select(x => (byte)x))
{
var vectorCh = new Vector<byte>(ch);
var hit = iterator;
hit.Seek(ref vectorCh);
hit.Seek(ch);
Assert.Equal(ch, iterator.GetLength(hit));
hit = iterator;
hit.Seek(ref vectorCh, ref vectorMaxValues);
hit.Seek(ch, byte.MaxValue);
Assert.Equal(ch, iterator.GetLength(hit));
hit = iterator;
hit.Seek(ref vectorMaxValues, ref vectorCh);
hit.Seek(byte.MaxValue, ch);
Assert.Equal(ch, iterator.GetLength(hit));
hit = iterator;
hit.Seek(ref vectorCh, ref vectorMaxValues, ref vectorMaxValues);
hit.Seek(ch, byte.MaxValue, byte.MaxValue);
Assert.Equal(ch, iterator.GetLength(hit));
hit = iterator;
hit.Seek(ref vectorMaxValues, ref vectorCh, ref vectorMaxValues);
hit.Seek(byte.MaxValue, ch, byte.MaxValue);
Assert.Equal(ch, iterator.GetLength(hit));
hit = iterator;
hit.Seek(ref vectorCh, ref vectorMaxValues, ref vectorMaxValues);
hit.Seek(ch, byte.MaxValue, byte.MaxValue);
Assert.Equal(ch, iterator.GetLength(hit));
}
@ -77,35 +73,31 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
block3.Array[block3.End++] = ch;
}
var vectorMaxValues = new Vector<byte>(byte.MaxValue);
var iterator = block1.GetIterator();
foreach (var ch in Enumerable.Range(0, 256).Select(x => (byte)x))
{
var vectorCh = new Vector<byte>(ch);
var hit = iterator;
hit.Seek(ref vectorCh);
hit.Seek(ch);
Assert.Equal(ch, iterator.GetLength(hit));
hit = iterator;
hit.Seek(ref vectorCh, ref vectorMaxValues);
hit.Seek(ch, byte.MaxValue);
Assert.Equal(ch, iterator.GetLength(hit));
hit = iterator;
hit.Seek(ref vectorMaxValues, ref vectorCh);
hit.Seek(byte.MaxValue, ch);
Assert.Equal(ch, iterator.GetLength(hit));
hit = iterator;
hit.Seek(ref vectorCh, ref vectorMaxValues, ref vectorMaxValues);
hit.Seek(ch, byte.MaxValue, byte.MaxValue);
Assert.Equal(ch, iterator.GetLength(hit));
hit = iterator;
hit.Seek(ref vectorMaxValues, ref vectorCh, ref vectorMaxValues);
hit.Seek(byte.MaxValue, ch, byte.MaxValue);
Assert.Equal(ch, iterator.GetLength(hit));
hit = iterator;
hit.Seek(ref vectorMaxValues, ref vectorMaxValues, ref vectorCh);
hit.Seek(byte.MaxValue, byte.MaxValue, ch);
Assert.Equal(ch, iterator.GetLength(hit));
}

View File

@ -78,21 +78,15 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
int found = -1;
if (searchFor.Length == 1)
{
var search0 = new Vector<byte>((byte) searchFor[0]);
found = begin.Seek(ref search0);
found = begin.Seek((byte)searchFor[0]);
}
else if (searchFor.Length == 2)
{
var search0 = new Vector<byte>((byte) searchFor[0]);
var search1 = new Vector<byte>((byte) searchFor[1]);
found = begin.Seek(ref search0, ref search1);
found = begin.Seek((byte)searchFor[0], (byte)searchFor[1]);
}
else if (searchFor.Length == 3)
{
var search0 = new Vector<byte>((byte) searchFor[0]);
var search1 = new Vector<byte>((byte) searchFor[1]);
var search2 = new Vector<byte>((byte) searchFor[2]);
found = begin.Seek(ref search0, ref search1, ref search2);
found = begin.Seek((byte)searchFor[0], (byte)searchFor[1], (byte)searchFor[2]);
}
else
{
@ -739,7 +733,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
try
{
// Arrange
var seekVector = new Vector<byte>((byte)seek);
block = _pool.Lease();
var chars = input.ToString().ToCharArray().Select(c => (byte)c).ToArray();
@ -749,7 +742,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
// Act
int bytesScanned;
var returnValue = scan.Seek(ref seekVector, out bytesScanned, limit);
var returnValue = scan.Seek((byte)seek, out bytesScanned, limit);
// Assert
Assert.Equal(expectedBytesScanned, bytesScanned);
@ -779,8 +772,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
try
{
// Arrange
var seekVector = new Vector<byte>((byte)seek);
var input1 = input.Substring(0, input.Length / 2);
block1 = _pool.Lease();
var chars1 = input1.ToCharArray().Select(c => (byte)c).ToArray();
@ -801,7 +792,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
// Act
int bytesScanned;
var returnValue = scan.Seek(ref seekVector, out bytesScanned, limit);
var returnValue = scan.Seek((byte)seek, out bytesScanned, limit);
// Assert
Assert.Equal(expectedBytesScanned, bytesScanned);
@ -835,9 +826,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
try
{
// Arrange
var seekVector = new Vector<byte>((byte)seek);
var limitAtVector = new Vector<byte>((byte)limitAt);
var afterSeekVector = new Vector<byte>((byte)'B');
var afterSeek = (byte)'B';
block = _pool.Lease();
var chars = input.ToCharArray().Select(c => (byte)c).ToArray();
@ -852,13 +841,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var end = scan1;
// Act
var endReturnValue = end.Seek(ref limitAtVector);
var returnValue1 = scan1.Seek(ref seekVector, ref end);
var returnValue2_1 = scan2_1.Seek(ref seekVector, ref afterSeekVector, ref end);
var returnValue2_2 = scan2_2.Seek(ref afterSeekVector, ref seekVector, ref end);
var returnValue3_1 = scan3_1.Seek(ref seekVector, ref afterSeekVector, ref afterSeekVector, ref end);
var returnValue3_2 = scan3_2.Seek(ref afterSeekVector, ref seekVector, ref afterSeekVector, ref end);
var returnValue3_3 = scan3_3.Seek(ref afterSeekVector, ref afterSeekVector, ref seekVector, ref end);
var endReturnValue = end.Seek((byte)limitAt);
var returnValue1 = scan1.Seek((byte)seek, ref end);
var returnValue2_1 = scan2_1.Seek((byte)seek, afterSeek, ref end);
var returnValue2_2 = scan2_2.Seek(afterSeek, (byte)seek, ref end);
var returnValue3_1 = scan3_1.Seek((byte)seek, afterSeek, afterSeek, ref end);
var returnValue3_2 = scan3_2.Seek(afterSeek, (byte)seek, afterSeek, ref end);
var returnValue3_3 = scan3_3.Seek(afterSeek, afterSeek, (byte)seek, ref end);
// Assert
Assert.Equal(input.Contains(limitAt) ? limitAt : -1, endReturnValue);
@ -902,9 +891,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
try
{
// Arrange
var seekVector = new Vector<byte>((byte)seek);
var limitAtVector = new Vector<byte>((byte)limitAt);
var afterSeekVector = new Vector<byte>((byte)'B');
var afterSeek = (byte)'B';
var input1 = input.Substring(0, input.Length / 2);
block1 = _pool.Lease();
@ -931,13 +918,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var end = scan1;
// Act
var endReturnValue = end.Seek(ref limitAtVector);
var returnValue1 = scan1.Seek(ref seekVector, ref end);
var returnValue2_1 = scan2_1.Seek(ref seekVector, ref afterSeekVector, ref end);
var returnValue2_2 = scan2_2.Seek(ref afterSeekVector, ref seekVector, ref end);
var returnValue3_1 = scan3_1.Seek(ref seekVector, ref afterSeekVector, ref afterSeekVector, ref end);
var returnValue3_2 = scan3_2.Seek(ref afterSeekVector, ref seekVector, ref afterSeekVector, ref end);
var returnValue3_3 = scan3_3.Seek(ref afterSeekVector, ref afterSeekVector, ref seekVector, ref end);
var endReturnValue = end.Seek((byte)limitAt);
var returnValue1 = scan1.Seek((byte)seek, ref end);
var returnValue2_1 = scan2_1.Seek((byte)seek, afterSeek, ref end);
var returnValue2_2 = scan2_2.Seek(afterSeek, (byte)seek, ref end);
var returnValue3_1 = scan3_1.Seek((byte)seek, afterSeek, afterSeek, ref end);
var returnValue3_2 = scan3_2.Seek(afterSeek, (byte)seek, afterSeek, ref end);
var returnValue3_3 = scan3_3.Seek(afterSeek, afterSeek, (byte)seek, ref end);
// Assert
Assert.Equal(input.Contains(limitAt) ? limitAt : -1, endReturnValue);