// This test verifies that optimization for known cursors works and // avoids additional walk but it's only valid for multi segmented buffers public void ReadCursorSeekDoesNotCheckEndIfTrustingEnd() { var buffer = Factory.CreateOfSize(3); var buffer2 = Factory.CreateOfSize(3); ReadOnlyBuffer <byte> .Seek(buffer.Start, buffer2.End, 2, false); }
public void MemorySeek(string raw, string search, char expectResult, int expectIndex) { var cursors = Factory.CreateWithContent(raw); Position start = cursors.Start; Position end = cursors.End; Position result = default; var searchFor = search.ToCharArray(); int found = -1; if (searchFor.Length == 1) { found = ReadOnlyBuffer.Seek(start, end, out result, (byte)searchFor[0]); } else if (searchFor.Length == 2) { found = ReadOnlyBuffer.Seek(start, end, out result, (byte)searchFor[0], (byte)searchFor[1]); } else if (searchFor.Length == 3) { found = ReadOnlyBuffer.Seek(start, end, out result, (byte)searchFor[0], (byte)searchFor[1], (byte)searchFor[2]); } else { Assert.False(true, "Invalid test sample."); } Assert.Equal(expectResult, found); Assert.Equal(cursors.Slice(result).ToArray(), Encoding.ASCII.GetBytes(raw.Substring(expectIndex))); }
public void ReadCursorSeekChecksEndIfNotTrustingEnd() { var buffer = Factory.CreateOfSize(3); var buffer2 = Factory.CreateOfSize(3); Assert.Throws <InvalidOperationException>(() => ReadOnlyBuffer <byte> .Seek(buffer.Start, buffer2.End, 2, true)); }
public void TestSeekIteratorLimitWithinSameBlock(string input, char seek, char limitAfter, int expectedReturnValue) { // Arrange var afterSeek = (byte)'B'; var buffer = Factory.CreateWithContent(input); var start = buffer.Start; var scan1 = buffer.Start; var veryEnd = buffer.End; var scan2_1 = scan1; var scan2_2 = scan1; var scan3_1 = scan1; var scan3_2 = scan1; var scan3_3 = scan1; var end = buffer.End; // Act var endReturnValue = ReadOnlyBuffer.Seek(start, veryEnd, out end, (byte)limitAfter); if (endReturnValue != -1) { end = buffer.Slice(end, 1).End; } var returnValue1 = ReadOnlyBuffer.Seek(start, end, out scan1, (byte)seek); var returnValue2_1 = ReadOnlyBuffer.Seek(start, end, out scan2_1, (byte)seek, afterSeek); var returnValue2_2 = ReadOnlyBuffer.Seek(start, end, out scan2_2, afterSeek, (byte)seek); var returnValue3_1 = ReadOnlyBuffer.Seek(start, end, out scan3_1, (byte)seek, afterSeek, afterSeek); var returnValue3_2 = ReadOnlyBuffer.Seek(start, end, out scan3_2, afterSeek, (byte)seek, afterSeek); var returnValue3_3 = ReadOnlyBuffer.Seek(start, end, out scan3_3, afterSeek, afterSeek, (byte)seek); // Assert Assert.Equal(input.Contains(limitAfter) ? limitAfter : -1, endReturnValue); Assert.Equal(expectedReturnValue, returnValue1); Assert.Equal(expectedReturnValue, returnValue2_1); Assert.Equal(expectedReturnValue, returnValue2_2); Assert.Equal(expectedReturnValue, returnValue3_1); Assert.Equal(expectedReturnValue, returnValue3_2); Assert.Equal(expectedReturnValue, returnValue3_3); if (expectedReturnValue != -1) { var expectedEndIndex = input.IndexOf(seek); Assert.Equal(buffer.Slice(scan1).ToArray(), Encoding.ASCII.GetBytes(input.Substring(expectedEndIndex))); Assert.Equal(buffer.Slice(scan2_1).ToArray(), Encoding.ASCII.GetBytes(input.Substring(expectedEndIndex))); Assert.Equal(buffer.Slice(scan2_2).ToArray(), Encoding.ASCII.GetBytes(input.Substring(expectedEndIndex))); Assert.Equal(buffer.Slice(scan3_1).ToArray(), Encoding.ASCII.GetBytes(input.Substring(expectedEndIndex))); Assert.Equal(buffer.Slice(scan3_2).ToArray(), Encoding.ASCII.GetBytes(input.Substring(expectedEndIndex))); Assert.Equal(buffer.Slice(scan3_3).ToArray(), Encoding.ASCII.GetBytes(input.Substring(expectedEndIndex))); } }
private void ParseExtension(ReadOnlyBuffer buffer, out Position consumed, out Position examined) { // Chunk-extensions not currently parsed // Just drain the data consumed = buffer.Start; examined = buffer.Start; do { Position extensionCursor; if (ReadOnlyBuffer.Seek(buffer.Start, buffer.End, out extensionCursor, ByteCR) == -1) { // End marker not found yet consumed = buffer.End; examined = buffer.End; AddAndCheckConsumedBytes(buffer.Length); return; } ; var charsToByteCRExclusive = buffer.Slice(0, extensionCursor).Length; var sufixBuffer = buffer.Slice(extensionCursor); if (sufixBuffer.Length < 2) { consumed = extensionCursor; examined = buffer.End; AddAndCheckConsumedBytes(charsToByteCRExclusive); return; } sufixBuffer = sufixBuffer.Slice(0, 2); var sufixSpan = sufixBuffer.ToSpan(); if (sufixSpan[1] == '\n') { // We consumed the \r\n at the end of the extension, so switch modes. _mode = _inputLength > 0 ? Mode.Data : Mode.Trailer; consumed = sufixBuffer.End; examined = sufixBuffer.End; AddAndCheckConsumedBytes(charsToByteCRExclusive + 2); } else { // Don't consume suffixSpan[1] in case it is also a \r. buffer = buffer.Slice(charsToByteCRExclusive + 1); consumed = extensionCursor; AddAndCheckConsumedBytes(charsToByteCRExclusive + 1); } } while (_mode == Mode.Extension); }
public void SegmentStartIsConsideredInBoundsCheck() { // 0 50 100 0 50 100 // [ ##############] -> [############## ] // ^c1 ^c2 var bufferSegment1 = new BufferSegment(); bufferSegment1.SetMemory(new OwnedArray <byte>(new byte[100]), 50, 99); var bufferSegment2 = new BufferSegment(); bufferSegment2.SetMemory(new OwnedArray <byte>(new byte[100]), 0, 50); bufferSegment1.SetNext(bufferSegment2); var readableBuffer = new ReadOnlyBuffer <byte>(bufferSegment1, 0, bufferSegment2, 50); var c1 = readableBuffer.Seek(readableBuffer.Start, 25); // segment 1 index 75 var c2 = readableBuffer.Seek(readableBuffer.Start, 55); // segment 2 index 5 var sliced = readableBuffer.Slice(c1, c2); Assert.Equal(30, sliced.Length); }
private static void FindAllNewLines(ReadOnlyBuffer buffer) { var start = buffer.Start; var end = buffer.End; while (true) { if (ReadOnlyBuffer.Seek(start, end, out var found, (byte)'\n') == -1) { break; } start = buffer.Move(found, 1); } }
public void MovePrefersNextSegment() { var bufferSegment1 = new BufferSegment(); bufferSegment1.SetMemory(new OwnedArray <byte>(new byte[100]), 49, 99); var bufferSegment2 = new BufferSegment(); bufferSegment2.SetMemory(new OwnedArray <byte>(new byte[100]), 0, 0); bufferSegment1.SetNext(bufferSegment2); var readableBuffer = new ReadOnlyBuffer <byte>(bufferSegment1, 0, bufferSegment2, 0); var c1 = readableBuffer.Seek(readableBuffer.Start, 50); Assert.Equal(0, c1.Index); Assert.Equal(bufferSegment2, c1.Segment); }
private static void FindAllNewLinesReadableBufferReader(ReadOnlyBuffer buffer) { var reader = BufferReader.Create(buffer); var end = buffer.End; while (!reader.End) { var span = reader.Span; // Trim the start if we have an index if (reader.Index > 0) { span = span.Slice(reader.Index); } while (span.Length > 0) { var length = span.IndexOf((byte)'\n'); var skip = length; if (length == -1) { var current = reader.Position; if (ReadOnlyBuffer.Seek(current, end, out var found, (byte)'\n') == -1) { // We're done return; } length = span.Length; skip = (int)buffer.Slice(current, found).Length + 1; } else { length += 1; skip = length; } span = span.Slice(length); reader.Skip(skip); } } }
public void TestSeekByteLimitWithinSameBlock(string input, char seek, int limit, int expectedBytesScanned, int expectedReturnValue) { // Arrange var buffer = Factory.CreateWithContent(input); // Act var end = limit > input.Length ? buffer.End : buffer.Slice(0, limit).End; var returnValue = ReadOnlyBuffer.Seek(buffer.Start, end, out Position result, (byte)seek); var returnValue_1 = ReadOnlyBuffer.Seek(buffer.Start, end, out result, (byte)seek, (byte)seek); var returnValue_2 = ReadOnlyBuffer.Seek(buffer.Start, end, out result, (byte)seek, (byte)seek, (byte)seek); // Assert Assert.Equal(expectedReturnValue, returnValue); Assert.Equal(expectedReturnValue, returnValue_1); Assert.Equal(expectedReturnValue, returnValue_2); if (expectedReturnValue != -1) { Assert.Equal(buffer.Slice(result).ToArray(), Encoding.ASCII.GetBytes(input.Substring(expectedBytesScanned - 1))); } }
public ParseResult ParseMessage(ReadOnlyBuffer buffer, out Position consumed, out Position examined, out byte[] message) { consumed = buffer.Start; examined = buffer.End; message = null; var start = consumed; var end = examined; while (buffer.Length > 0) { if (ReadOnlyBuffer.Seek(start, end, out var lineEnd, ByteLF) == -1) { // For the case of data: Foo\r\n\r\<Anytine except \n> if (_internalParserState == InternalParseState.ReadEndOfMessage) { if (ConvertBufferToSpan(buffer.Slice(start, buffer.End)).Length > 1) { throw new FormatException("Expected a \\r\\n frame ending"); } } // Partial message. We need to read more. return(ParseResult.Incomplete); } lineEnd = buffer.Move(lineEnd, 1); var line = ConvertBufferToSpan(buffer.Slice(start, lineEnd)); buffer = buffer.Slice(line.Length); if (line.Length <= 1) { throw new FormatException("There was an error in the frame format"); } // Skip comments if (line[0] == ByteColon) { start = lineEnd; consumed = lineEnd; continue; } if (IsMessageEnd(line)) { _internalParserState = InternalParseState.ReadEndOfMessage; } // To ensure that the \n was preceded by a \r // since messages can't contain \n. // data: foo\n\bar should be encoded as // data: foo\r\n // data: bar\r\n else if (line[line.Length - _sseLineEnding.Length] != ByteCR) { throw new FormatException("Unexpected '\n' in message. A '\n' character can only be used as part of the newline sequence '\r\n'"); } else { EnsureStartsWithDataPrefix(line); } var payload = Array.Empty <byte>(); switch (_internalParserState) { case InternalParseState.ReadMessagePayload: EnsureStartsWithDataPrefix(line); // Slice away the 'data: ' var payloadLength = line.Length - (_dataPrefix.Length + _sseLineEnding.Length); var newData = line.Slice(_dataPrefix.Length, payloadLength).ToArray(); _data.Add(newData); start = lineEnd; consumed = lineEnd; break; case InternalParseState.ReadEndOfMessage: if (_data.Count == 1) { payload = _data[0]; } else if (_data.Count > 1) { // Find the final size of the payload var payloadSize = 0; foreach (var dataLine in _data) { payloadSize += dataLine.Length; } payloadSize += _newLine.Length * _data.Count; // Allocate space in the payload buffer for the data and the new lines. // Subtract newLine length because we don't want a trailing newline. payload = new byte[payloadSize - _newLine.Length]; var offset = 0; foreach (var dataLine in _data) { dataLine.CopyTo(payload, offset); offset += dataLine.Length; if (offset < payload.Length) { _newLine.CopyTo(payload, offset); offset += _newLine.Length; } } } message = payload; consumed = lineEnd; examined = consumed; return(ParseResult.Completed); } if (buffer.Length > 0 && buffer.First.Span[0] == ByteCR) { _internalParserState = InternalParseState.ReadEndOfMessage; } } return(ParseResult.Incomplete); }
public Position Seek(Position origin, long offset) => _buffer.Seek(origin, offset);
/// <summary> /// Searches for a byte in the <see cref="ReadOnlyBuffer"/> and returns a sliced <see cref="ReadOnlyBuffer"/> that /// contains all data up to and excluding the byte, and a <see cref="Position"/> that points to the byte. /// </summary> /// <param name="b1">The first byte to search for</param> /// <param name="slice">A <see cref="ReadOnlyBuffer"/> slice that contains all data up to and excluding the first byte.</param> /// <param name="cursor">A <see cref="Position"/> that points to the second byte</param> /// <returns>True if the byte sequence was found, false if not found</returns> public static bool TrySliceTo(this ReadOnlyBuffer <byte> buffer, byte b1, out ReadOnlyBuffer <byte> slice, out Position cursor) { if (buffer.IsEmpty) { slice = default; cursor = default; return(false); } var byte0Vector = GetVector(b1); var seek = 0; foreach (var memory in buffer) { var currentSpan = memory.Span; var found = false; if (Vector.IsHardwareAccelerated) { while (currentSpan.Length >= VectorWidth) { var data = ReadMachineEndian <Vector <byte> >(currentSpan); var byte0Equals = Vector.Equals(data, byte0Vector); if (byte0Equals.Equals(Vector <byte> .Zero)) { currentSpan = currentSpan.Slice(VectorWidth); seek += VectorWidth; } else { var index = FindFirstEqualByte(ref byte0Equals); seek += index; found = true; break; } } } if (!found) { // Slow search for (int i = 0; i < currentSpan.Length; i++) { if (currentSpan[i] == b1) { found = true; break; } seek++; } } if (found) { cursor = buffer.Seek(buffer.Start, seek); slice = buffer.Slice(buffer.Start, cursor); return(true); } } slice = default; cursor = default; return(false); }