* Unified Readers
* Removed PositionOf from reader
* Added tests for CopyTo
* reacted to changes to ReadOnlyBuffer
* Renamed CopyTo to Peek
This commit is contained in:
Krzysztof Cwalina 2018-01-09 08:55:50 -08:00 коммит произвёл GitHub
Родитель 00bc61f856
Коммит 1fecfe7350
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 373 добавлений и 474 удалений

Просмотреть файл

@ -0,0 +1,49 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Buffers.Binary;
using System.Collections.Sequences;
namespace System.Buffers
{
public static partial class BufferReaderExtensions
{
public static bool TryRead<TSequence>(ref BufferReader<TSequence> reader, out int value, bool littleEndian = false)
where TSequence : ISequence<ReadOnlyMemory<byte>>
{
var unread = reader.UnreadSegment;
if (littleEndian)
{
if (BinaryPrimitives.TryReadInt32LittleEndian(unread, out value))
{
reader.Skip(sizeof(int));
return true;
}
}
else if (BinaryPrimitives.TryReadInt32BigEndian(unread, out value))
{
reader.Skip(sizeof(int));
return true;
}
Span<byte> tempSpan = stackalloc byte[4];
var copied = BufferReader.Peek(reader, tempSpan);
if (copied < 4)
{
value = default;
return false;
}
if (littleEndian)
{
value = BinaryPrimitives.ReadInt32LittleEndian(tempSpan);
}
else
{
value = BinaryPrimitives.ReadInt32BigEndian(tempSpan);
}
reader.Skip(sizeof(int));
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,61 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Sequences;
namespace System.Buffers
{
// TODO: the TryReadUntill methods are very inneficient. We need to fix that.
public static partial class BufferReaderExtensions
{
public static bool TryReadUntill(ref BufferReader<ReadOnlyBytes> reader, out ReadOnlyBytes bytes, byte delimiter)
{
var copy = reader;
var start = reader.Position;
while (!reader.End) {
Position end = reader.Position;
if(reader.Take() == delimiter)
{
bytes = new ReadOnlyBytes(start, end);
return true;
}
}
reader = copy;
bytes = default;
return false;
}
public static bool TryReadUntill(ref BufferReader<ReadOnlyBytes> reader, out ReadOnlyBytes bytes, ReadOnlySpan<byte> delimiter)
{
if (delimiter.Length == 0)
{
bytes = ReadOnlyBytes.Empty;
return true;
}
int matched = 0;
var copy = reader;
var start = reader.Position;
var end = reader.Position;
while (!reader.End)
{
if (reader.Take() == delimiter[matched]) {
matched++;
}
else
{
end = reader.Position;
matched = 0;
}
if(matched >= delimiter.Length)
{
bytes = new ReadOnlyBytes(start, end);
return true;
}
}
reader = copy;
bytes = default;
return false;
}
}
}

Просмотреть файл

@ -0,0 +1,83 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Buffers.Text;
using System.Collections.Sequences;
namespace System.Buffers
{
public static partial class BufferReaderExtensions
{
public static bool TryParse<TSequence>(ref BufferReader<TSequence> reader, out bool value)
where TSequence : ISequence<ReadOnlyMemory<byte>>
{
var unread = reader.UnreadSegment;
if (Utf8Parser.TryParse(unread, out value, out int consumed))
{
if (unread.Length > consumed)
{
reader.Skip(consumed);
return true;
}
}
Span<byte> tempSpan = stackalloc byte[5];
var copied = BufferReader.Peek(reader, tempSpan);
if (Utf8Parser.TryParse(tempSpan.Slice(0, copied), out value, out consumed))
{
reader.Skip(consumed);
return true;
}
return false;
}
public static bool TryParse<TSequence>(ref BufferReader<TSequence> reader, out int value)
where TSequence : ISequence<ReadOnlyMemory<byte>>
{
var unread = reader.UnreadSegment;
if (Utf8Parser.TryParse(unread, out value, out int consumed))
{
if (unread.Length > consumed)
{
reader.Skip(consumed);
return true;
}
}
Span<byte> tempSpan = stackalloc byte[15];
var copied = BufferReader.Peek(reader, tempSpan);
if (Utf8Parser.TryParse(tempSpan.Slice(0, copied), out value, out consumed))
{
reader.Skip(consumed);
return true;
}
return false;
}
public static bool TryParse<TSequence>(ref BufferReader<TSequence> reader, out ulong value)
where TSequence : ISequence<ReadOnlyMemory<byte>>
{
var unread = reader.UnreadSegment;
if (Utf8Parser.TryParse(unread, out value, out int consumed))
{
if (unread.Length > consumed)
{
reader.Skip(consumed);
return true;
}
}
Span<byte> tempSpan = stackalloc byte[30];
var copied = BufferReader.Peek(reader, tempSpan);
if (Utf8Parser.TryParse(tempSpan.Slice(0, copied), out value, out consumed))
{
reader.Skip(consumed);
return true;
}
return false;
}
}
}

Просмотреть файл

@ -283,7 +283,14 @@ namespace System.Buffers
{
var start = (int)position;
var length = _endIndex - (int)position;
item = new ReadOnlyMemory<byte>(array, start, length);
if (length == 0)
{
item = ReadOnlyMemory<byte>.Empty;
}
else
{
item = new ReadOnlyMemory<byte>(array, start, length);
}
if (advance) position = default;
return true;
}
@ -304,6 +311,16 @@ namespace System.Buffers
return true;
}
var om = _start as OwnedMemory<byte>;
if (om != null)
{
var start = (int)position;
var length = _endIndex - (int)position;
item = om.Memory.Slice(start, length);
if (advance) position = default;
return true;
}
throw new NotImplementedException();
}

Просмотреть файл

@ -1,406 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Buffers.Binary;
using System.Collections.Sequences;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace System.Buffers.Text
{
/// <summary>
/// Used to read text from byte buffers
/// </summary>
public ref struct BufferReader<TSequence> where TSequence : ISequence<ReadOnlyMemory<byte>>
{
readonly TSequence _bytes;
Position _currentSegmentPosition;
Position _nextSegmentPosition;
ReadOnlySpan<byte> _currentSpan;
int _currentSpanIndex;
// TODO: should there be a ctor that takes sequence + position?
// TODO: should there be a type that is sequence + position?
public BufferReader(TSequence bytes)
{
_bytes = bytes;
_nextSegmentPosition = bytes.Start;
_currentSegmentPosition = _nextSegmentPosition;
if(_bytes.TryGet(ref _nextSegmentPosition, out ReadOnlyMemory<byte> memory))
{
_currentSpan = memory.Span;
}
else
{
_currentSpan = ReadOnlySpan<byte>.Empty;
}
_currentSpanIndex = 0;
}
public BufferReader(ReadOnlyMemory<byte> bytes)
{
_bytes = default;
_nextSegmentPosition = default;
_currentSegmentPosition = default;
_currentSpan = bytes.Span;
_currentSpanIndex = 0;
}
public BufferReader(ReadOnlySpan<byte> bytes)
{
_bytes = default;
_nextSegmentPosition = default;
_currentSegmentPosition = default;
_currentSpan = bytes;
_currentSpanIndex = 0;
}
public bool IsEmpty {
get {
if (_currentSpan.Length - _currentSpanIndex > 0)
{
return false;
}
Position position = _nextSegmentPosition;
while (_bytes.TryGet(ref position, out ReadOnlyMemory<byte> next))
{
if (!next.IsEmpty) return false;
}
return true;
}
}
public bool TryPeek(out byte value)
{
if (_currentSpan.Length > _currentSpanIndex)
{
value = _currentSpan[_currentSpanIndex];
return true;
}
// TODO: this should try to advance
value = default;
return false;
}
public bool TryReadBytes(out ReadOnlyBytes bytes, byte delimiter)
{
var range = ReadRange(delimiter);
if(range.Start == default || range.End == default)
{
bytes = default;
return false;
}
var (startObj, startIndex) = range.Start.Get<object>();
var (endObj, endIndex) = range.End.Get<object>();
// TODO: this is a hack. Once we move this to System.Memory, we should remove
if (startObj == null || endObj == null)
{
bytes = default;
return false;
}
bytes = new ReadOnlyBytes(range.Start, range.End);
return true;
}
public bool TryReadBytes(out ReadOnlyBytes bytes, ReadOnlySpan<byte> delimiter)
{
var range = ReadRange(delimiter);
if (range.Start == default || range.End == default)
{
bytes = default;
return false;
}
bytes = new ReadOnlyBytes(range.Start, range.End);
return true;
}
PositionRange ReadRange(byte delimiter)
=> new PositionRange(Position, AdvanceToDelimiter(delimiter).GetValueOrDefault());
PositionRange ReadRange(ReadOnlySpan<byte> delimiter)
{
var range = new PositionRange(Position, PositionOf(delimiter).GetValueOrDefault());
if (range.End != default)
{
Advance(range.End);
Advance(delimiter.Length);
}
return range;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Advance(long count)
{
var unreadLength = _currentSpan.Length - _currentSpanIndex;
if (count < unreadLength) {
_currentSpanIndex += (int)count;
}
else {
AdvanceNextSegment(count, unreadLength);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Advance(int count)
{
var unreadLength = _currentSpan.Length - _currentSpanIndex;
if (count < unreadLength)
{
_currentSpanIndex += count;
}
else
{
AdvanceNextSegment(count, unreadLength);
}
}
private void AdvanceNextSegment(long count, int currentSegmentUnread)
{
if (!_bytes.TryGet(ref _nextSegmentPosition, out ReadOnlyMemory<byte> memory))
{
if (count > currentSegmentUnread) throw new ArgumentOutOfRangeException(nameof(count));
else
{
Debug.Assert(count == currentSegmentUnread);
_currentSpan = Span<byte>.Empty;
_currentSpanIndex = 0;
return;
}
}
_currentSpan = memory.Span;
_currentSpanIndex = 0;
Advance(count - currentSegmentUnread);
}
public void Advance(Position position)
{
_currentSegmentPosition = position;
_nextSegmentPosition = position;
if (_bytes.TryGet(ref _nextSegmentPosition, out ReadOnlyMemory<byte> memory))
{
_currentSpan = memory.Span;
}
else
{
_currentSpan = default;
}
_currentSpanIndex = 0;
}
#region Parsing Methods
// TODO: how to we chose the lengths of the temp buffers?
// TODO: these methods call the slow overloads of Parsers.Custom.TryParseXXX. Do we need fast path?
// TODO: these methods hardcode the format. Do we need this to be something that can be specified?
public bool TryParse(out bool value)
{
var unread = Unread;
if (Utf8Parser.TryParse(unread, out value, out int consumed))
{
if (unread.Length > consumed)
{
_currentSpanIndex += consumed;
return true;
}
}
Span<byte> tempSpan = stackalloc byte[5];
var copied = CopyTo(this, tempSpan);
if (Utf8Parser.TryParse(tempSpan.Slice(0, copied), out value, out consumed))
{
Advance(consumed);
return true;
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryParse(out int value)
{
var unread = Unread;
if (Utf8Parser.TryParse(unread, out value, out int consumed))
{
if (unread.Length > consumed) {
_currentSpanIndex += consumed;
return true;
}
}
return TryParseStraddling(out value);
}
private bool TryParseStraddling(out int value)
{
Span<byte> tempSpan = stackalloc byte[15];
var copied = CopyTo(this, tempSpan);
if (Utf8Parser.TryParse(tempSpan.Slice(0, copied), out value, out int consumed))
{
Advance(consumed);
return true;
}
return false;
}
public bool TryParse(out ulong value)
{
var unread = Unread;
if (Utf8Parser.TryParse(unread, out value, out int consumed))
{
if (unread.Length > consumed)
{
_currentSpanIndex += consumed;
return true;
}
}
Span<byte> tempSpan = stackalloc byte[30];
var copied = CopyTo(this, tempSpan);
if (Utf8Parser.TryParse(tempSpan.Slice(0, copied), out value, out consumed))
{
Advance(consumed);
return true;
}
return false;
}
#endregion
#region Binary Read APIs
public bool TryRead(out int value, bool littleEndian = false)
{
var unread = Unread;
if (littleEndian) {
if (BinaryPrimitives.TryReadInt32LittleEndian(unread, out value)) {
Advance(sizeof(int));
return true;
}
}
else if (BinaryPrimitives.TryReadInt32BigEndian(unread, out value)) {
Advance(sizeof(int));
return true;
}
Span<byte> span = stackalloc byte[4];
var copied = CopyTo(this, span);
if (copied < 4) {
value = default;
return false;
}
if (littleEndian) {
value = BinaryPrimitives.ReadInt32LittleEndian(span);
}
else {
value = BinaryPrimitives.ReadInt32BigEndian(span);
}
Advance(sizeof(int));
return true;
}
#endregion
ReadOnlySpan<byte> Unread => _currentSpan.Slice(_currentSpanIndex);
Position Position =>_currentSegmentPosition + _currentSpanIndex;
Position? AdvanceToDelimiter(byte value)
{
var unread = Unread;
var index = unread.IndexOf(value);
if (index != -1)
{
_currentSpanIndex += index;
var result = _currentSegmentPosition + _currentSpanIndex;
_currentSpanIndex++; // skip delimiter
return result;
}
var nextPosition = _nextSegmentPosition;
var currentPosition = _currentSegmentPosition;
var previousPosition = _nextSegmentPosition;
while (_bytes.TryGet(ref _nextSegmentPosition, out ReadOnlyMemory<byte> memory))
{
var span = memory.Span;
index = span.IndexOf(value);
if (index != -1)
{
_currentSegmentPosition = previousPosition;
_currentSpan = span;
_currentSpanIndex = index + 1;
return _currentSegmentPosition + index;
}
previousPosition = _nextSegmentPosition;
}
_nextSegmentPosition = nextPosition;
_currentSegmentPosition = currentPosition;
return null;
}
Position? PositionOf(ReadOnlySpan<byte> value)
{
var unread = Unread;
var index = unread.IndexOf(value);
if (index != -1)
{
return _currentSegmentPosition + (index + _currentSpanIndex);
}
index = unread.IndexOf(value[0]);
if(index != -1 && unread.Length - index < value.Length)
{
Span<byte> temp = stackalloc byte[value.Length];
int copied = Sequence.Copy(_bytes, _currentSegmentPosition + (_currentSpanIndex + index), temp);
if (copied < value.Length) return null;
if (temp.SequenceEqual(value)) return _currentSegmentPosition + (_currentSpanIndex + index);
else throw new NotImplementedException(); // need to check farther in this span
}
if (unread.Length == 0) return null;
throw new NotImplementedException();
}
static int CopyTo(BufferReader<TSequence> bytes, Span<byte> buffer)
{
var first = bytes.Unread;
if (first.Length > buffer.Length)
{
first.Slice(0, buffer.Length).CopyTo(buffer);
return buffer.Length;
}
else if (first.Length == buffer.Length)
{
first.CopyTo(buffer);
return buffer.Length;
}
else
{
first.CopyTo(buffer);
return first.Length + Sequence.Copy(bytes._bytes, bytes._nextSegmentPosition, buffer.Slice(first.Length));
}
}
}
public static class BytesReader
{
public static BufferReader<T> Create<T>(T sequence) where T : ISequence<ReadOnlyMemory<byte>>
{
return new BufferReader<T>(sequence);
}
}
struct PositionRange
{
public readonly Position Start;
public readonly Position End;
public PositionRange(Position start, Position end)
{
Start = start;
End = end;
}
}
}

Просмотреть файл

@ -18,6 +18,8 @@ namespace System.Buffers
internal readonly Position BufferStart;
internal readonly Position BufferEnd;
public static readonly ReadOnlyBuffer Empty = new ReadOnlyBuffer(new byte[0]);
/// <summary>
/// Length of the <see cref="ReadOnlyBuffer"/> in bytes.
/// </summary>

Просмотреть файл

@ -12,15 +12,21 @@ namespace System.Buffers
{
return new BufferReader<TSequence>(buffer);
}
public static int Peek<TSequence>(BufferReader<TSequence> reader, Span<byte> destination)
where TSequence : ISequence<ReadOnlyMemory<byte>>
=> BufferReader<TSequence>.Peek(reader, destination);
}
public ref struct BufferReader<TSequence> where TSequence : ISequence<ReadOnlyMemory<byte>>
{
private ReadOnlySpan<byte> _currentSpan;
private int _index;
private TSequence _sequence;
private Position _currentPosition;
private Position _nextPosition;
private int _consumedBytes;
private bool _end;
@ -32,7 +38,7 @@ namespace System.Buffers
_sequence = buffer;
_currentPosition = _sequence.Start;
_nextPosition = _currentPosition;
_currentSpan = default;
_currentSpan = ReadOnlySpan<byte>.Empty;
MoveNext();
}
@ -42,7 +48,9 @@ namespace System.Buffers
public Position Position => _currentPosition + _index;
public ReadOnlySpan<byte> Span => _currentSpan;
public ReadOnlySpan<byte> CurrentSegment => _currentSpan;
public ReadOnlySpan<byte> UnreadSegment => _currentSpan.Slice(_index);
public int ConsumedBytes => _consumedBytes;
@ -64,8 +72,7 @@ namespace System.Buffers
return -1;
}
var value = _currentSpan[_index];
var value = _currentSpan[_index];
_index++;
_consumedBytes++;
@ -81,7 +88,7 @@ namespace System.Buffers
private void MoveNext()
{
var previous = _nextPosition;
while(_sequence.TryGet(ref _nextPosition, out var memory, true))
while (_sequence.TryGet(ref _nextPosition, out var memory, true))
{
_currentPosition = previous;
_currentSpan = memory.Span;
@ -94,36 +101,70 @@ namespace System.Buffers
_end = true;
}
public void Skip(int length)
public void Skip(int byteCount)
{
if (length < 0)
if (byteCount < 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
}
_consumedBytes += length;
_consumedBytes += byteCount;
while (!_end && length > 0)
while (!_end && byteCount > 0)
{
if ((_index + length) < _currentSpan.Length)
if ((_index + byteCount) < _currentSpan.Length)
{
_index += length;
length = 0;
_index += byteCount;
byteCount = 0;
break;
}
var remaining = (_currentSpan.Length - _index);
_index += remaining;
length -= remaining;
byteCount -= remaining;
MoveNext();
}
if (length > 0)
if (byteCount > 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
}
}
internal static int Peek(BufferReader<TSequence> bytes, Span<byte> destination)
{
var first = bytes.UnreadSegment;
if (first.Length > destination.Length)
{
first.Slice(0, destination.Length).CopyTo(destination);
return destination.Length;
}
else if (first.Length == destination.Length)
{
first.CopyTo(destination);
return destination.Length;
}
else
{
first.CopyTo(destination);
int copied = first.Length;
var next = bytes._nextPosition;
while (bytes._sequence.TryGet(ref next, out ReadOnlyMemory<byte> nextSegment, true))
{
var nextSpan = nextSegment.Span;
if (nextSpan.Length > 0)
{
var toCopy = Math.Min(nextSpan.Length, destination.Length - copied);
nextSpan.Slice(0, toCopy).CopyTo(destination.Slice(copied));
copied += toCopy;
if (copied >= destination.Length) break;
}
}
return copied;
}
}
}
}

Просмотреть файл

@ -245,7 +245,7 @@ namespace System.Text.Http.Parser
{
while (!reader.End)
{
var span = reader.Span;
var span = reader.CurrentSegment;
var remaining = span.Length - reader.Index;
fixed (byte* pBuffer = &MemoryMarshal.GetReference(span))

Просмотреть файл

@ -32,11 +32,11 @@ public class BytesReaderBench
static void ParseInt32BytesReader()
{
foreach (var iteration in Benchmark.Iterations) {
var reader = BytesReader.Create(s_readOnlyBytes);
var reader = BufferReader.Create(s_readOnlyBytes);
using (iteration.StartMeasurement()) {
while(reader.TryParse(out int value)) {
reader.Advance(1);
while(BufferReaderExtensions.TryParse(ref reader, out int value)) {
reader.Skip(1);
}
}
}
@ -46,11 +46,11 @@ public class BytesReaderBench
static void ParseInt32BytesRangeReader()
{
foreach (var iteration in Benchmark.Iterations) {
var reader = BytesReader.Create(s_bytesRange);
var reader = BufferReader.Create(s_bytesRange);
using (iteration.StartMeasurement()) {
while (reader.TryParse(out int value)) {
reader.Advance(1);
while (BufferReaderExtensions.TryParse(ref reader, out int value)) {
reader.Skip(1);
}
}
}
@ -84,7 +84,7 @@ public class BytesReaderBench
using (iteration.StartMeasurement())
{
while(Utf8Parser.TryParse(reader.Span.Slice(reader.ConsumedBytes), out int value, out int consumed)){
while(Utf8Parser.TryParse(reader.CurrentSegment.Slice(reader.ConsumedBytes), out int value, out int consumed)){
reader.Skip(consumed + 1);
}
}

Просмотреть файл

@ -14,15 +14,15 @@ namespace System.Buffers.Tests
public void SingleSegmentBytesReader()
{
ReadOnlyBytes bytes = Create("AB CD#EF&&");
var reader = BytesReader.Create(bytes);
var reader = BufferReader.Create(bytes);
Assert.True(reader.TryReadBytes(out var ab, (byte)' '));
Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var ab, (byte)' '));
Assert.Equal("AB", ab.ToString(SymbolTable.InvariantUtf8));
Assert.True(reader.TryReadBytes(out var cd, (byte)'#'));
Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var cd, (byte)'#'));
Assert.Equal("CD", cd.ToString(SymbolTable.InvariantUtf8));
Assert.True(reader.TryReadBytes(out var ef, new byte[] { (byte)'&', (byte)'&' }));
Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var ef, new byte[] { (byte)'&', (byte)'&' }));
Assert.Equal("EF", ef.ToString(SymbolTable.InvariantUtf8));
}
@ -40,27 +40,27 @@ namespace System.Buffers.Tests
new byte[] { 1, 2, 3, 4 },
});
var reader = BytesReader.Create(bytes);
var reader = BufferReader.Create(bytes);
Assert.True(reader.TryReadBytes(out var bytesValue, 2));
Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var bytesValue, 2));
var span = bytesValue.ToSpan();
Assert.Equal(0, span[0]);
Assert.Equal(1, span[1]);
Assert.True(reader.TryReadBytes(out bytesValue, 5));
Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out bytesValue, 5));
span = bytesValue.ToSpan();
Assert.Equal(3, span[0]);
Assert.Equal(4, span[1]);
Assert.True(reader.TryReadBytes(out bytesValue, new byte[] { 8, 8 }));
Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out bytesValue, new byte[] { 8, 8 }));
span = bytesValue.ToSpan();
Assert.Equal(6, span[0]);
Assert.Equal(7, span[1]);
Assert.True(reader.TryRead(out int value, true));
Assert.True(BufferReaderExtensions.TryRead(ref reader, out int value, true));
Assert.Equal(BitConverter.ToInt32(new byte[] { 0, 1, 0, 2 }), value);
Assert.True(reader.TryRead(out value));
Assert.True(BufferReaderExtensions.TryRead(ref reader, out value));
Assert.Equal(BitConverter.ToInt32(new byte[] { 4, 3, 2, 1 }), value);
}
@ -68,12 +68,12 @@ namespace System.Buffers.Tests
public void MultiSegmentBytesReader()
{
ReadOnlyBytes bytes = Parse("A|B |CD|#EF&|&");
var reader = BytesReader.Create(bytes);
var reader = BufferReader.Create(bytes);
Assert.True(reader.TryReadBytes(out var ab, (byte)' '));
Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var ab, (byte)' '));
Assert.Equal("AB", ab.Utf8ToString());
Assert.True(reader.TryReadBytes(out var cd, (byte)'#'));
Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var cd, (byte)'#'));
Assert.Equal("CD", cd.Utf8ToString());
//Assert.True(reader.TryReadBytes(out var ef, new byte[] { (byte)'&', (byte)'&' }));
@ -84,42 +84,42 @@ namespace System.Buffers.Tests
public void EmptyBytesReader()
{
ReadOnlyBytes bytes = Create("");
var reader = BytesReader.Create(bytes);
Assert.False(reader.TryReadBytes(out var range, (byte)' '));
var reader = BufferReader.Create(bytes);
Assert.False(BufferReaderExtensions.TryReadUntill(ref reader, out var range, (byte)' '));
bytes = Parse("|");
reader = BytesReader.Create(bytes);
Assert.False(reader.TryReadBytes(out range, (byte)' '));
reader = BufferReader.Create(bytes);
Assert.False(BufferReaderExtensions.TryReadUntill(ref reader, out range, (byte)' '));
}
[Fact]
public void BytesReaderParse()
{
ReadOnlyBytes bytes = Parse("12|3Tr|ue|456Tr|ue7|89False|");
var reader = BytesReader.Create(bytes);
var reader = BufferReader.Create(bytes);
Assert.True(reader.TryParse(out ulong u64));
Assert.True(BufferReaderExtensions.TryParse(ref reader, out ulong u64));
Assert.Equal(123ul, u64);
Assert.True(reader.TryParse(out bool b));
Assert.True(BufferReaderExtensions.TryParse(ref reader, out bool b));
Assert.Equal(true, b);
Assert.True(reader.TryParse(out u64));
Assert.True(BufferReaderExtensions.TryParse(ref reader, out u64));
Assert.Equal(456ul, u64);
Assert.True(reader.TryParse(out b));
Assert.True(BufferReaderExtensions.TryParse(ref reader, out b));
Assert.Equal(true, b);
Assert.True(reader.TryParse(out u64));
Assert.True(BufferReaderExtensions.TryParse(ref reader, out u64));
Assert.Equal(789ul, u64);
Assert.True(reader.TryParse(out b));
Assert.True(BufferReaderExtensions.TryParse(ref reader, out b));
Assert.Equal(false, b);
}
static byte[] s_eol = new byte[] { (byte)'\r', (byte)'\n' };
[Fact]
[Fact(Skip = "this needs to be redone; given we are unifying ROBs and readers")]
static void BytesReaderBenchmarkBaseline()
{
int sections = 10;
@ -130,24 +130,24 @@ namespace System.Buffers.Tests
}
var data = Encoding.UTF8.GetBytes(sb.ToString());
var readOnlyBytes = new ReadOnlyBytes(data);
var bytesRange = new ReadOnlyBytes(data);
var readOnlyBytes = new ReadOnlyBuffer(data);
var bytesRange = new ReadOnlyBuffer(data);
var robReader = BytesReader.Create(readOnlyBytes);
var robReader = BufferReader.Create(readOnlyBytes);
long robSum = 0;
while (robReader.TryParse(out int value))
while (BufferReaderExtensions.TryParse(ref robReader, out int value))
{
robSum += value;
robReader.Advance(1);
robReader.Skip(1);
}
var brReader = BytesReader.Create(bytesRange);
var brReader = BufferReader.Create(bytesRange);
long brSum = 0;
while (brReader.TryParse(out int value))
while (BufferReaderExtensions.TryParse(ref brReader, out int value))
{
brSum += value;
brReader.Advance(1);
brReader.Skip(1);
}
Assert.Equal(robSum, brSum);

Просмотреть файл

@ -33,12 +33,12 @@ namespace System.IO.Pipelines.Tests
var reader = BufferReader.Create(BufferUtilities.CreateBuffer(new byte[] { 1, 2, 3, 4, 5 }));
reader.Skip(2);
Assert.Equal(2, reader.Index);
Assert.Equal(3, reader.Span[reader.Index]);
Assert.Equal(3, reader.CurrentSegment[reader.Index]);
Assert.Equal(3, reader.Peek());
reader.Skip(2);
Assert.Equal(5, reader.Peek());
Assert.Equal(4, reader.Index);
Assert.Equal(5, reader.Span[reader.Index]);
Assert.Equal(5, reader.CurrentSegment[reader.Index]);
}
[Fact]
@ -46,10 +46,10 @@ namespace System.IO.Pipelines.Tests
{
var reader = BufferReader.Create(Factory.CreateWithContent(new byte[] { 1, 2 }));
Assert.Equal(0, reader.Index);
Assert.Equal(1, reader.Span[reader.Index]);
Assert.Equal(1, reader.CurrentSegment[reader.Index]);
Assert.Equal(1, reader.Take());
Assert.Equal(1, reader.Index);
Assert.Equal(2, reader.Span[reader.Index]);
Assert.Equal(2, reader.CurrentSegment[reader.Index]);
Assert.Equal(2, reader.Take());
Assert.Equal(-1, reader.Take());
}
@ -174,7 +174,7 @@ namespace System.IO.Pipelines.Tests
var reader = BufferReader.Create(buffer);
reader.Skip(2);
Assert.Equal(3, reader.Span[reader.Index]);
Assert.Equal(3, reader.CurrentSegment[reader.Index]);
Assert.Equal(3, reader.Take());
}
@ -213,10 +213,10 @@ namespace System.IO.Pipelines.Tests
var buffer = Factory.CreateWithContent(new byte[] { 1, 2 });
var reader = BufferReader.Create(buffer);
Assert.Equal(1, reader.Span[reader.Index]);
Assert.Equal(1, reader.CurrentSegment[reader.Index]);
Assert.Equal(1, reader.Take());
Assert.Equal(2, reader.Span[reader.Index]);
Assert.Equal(2, reader.CurrentSegment[reader.Index]);
Assert.Equal(2, reader.Peek());
Assert.Equal(2, reader.Take());
Assert.Equal(-1, reader.Peek());
@ -230,7 +230,7 @@ namespace System.IO.Pipelines.Tests
var reader = BufferReader.Create(buffer);
Assert.Equal(0, reader.Index);
Assert.Equal(1, reader.Span.Length);
Assert.Equal(1, reader.CurrentSegment.Length);
Assert.Equal(1, reader.Peek());
Assert.Equal(1, reader.Take());
Assert.Equal(-1, reader.Peek());
@ -243,7 +243,7 @@ namespace System.IO.Pipelines.Tests
var reader = BufferReader.Create(Factory.CreateWithContent(new byte[] { }));
Assert.Equal(0, reader.Index);
Assert.Equal(0, reader.Span.Length);
Assert.Equal(0, reader.CurrentSegment.Length);
Assert.Equal(-1, reader.Peek());
Assert.Equal(-1, reader.Take());
}
@ -289,15 +289,67 @@ namespace System.IO.Pipelines.Tests
var counter = 1;
while (!reader.End)
{
var span = reader.Span;
var span = reader.CurrentSegment;
for (int i = reader.Index; i < span.Length; i++)
{
Assert.Equal(counter++, reader.Span[i]);
Assert.Equal(counter++, reader.CurrentSegment[i]);
}
reader.Skip(span.Length);
}
Assert.Equal(buffer.Length, reader.ConsumedBytes);
}
[Fact]
public void CopyToLargerBufferWorks()
{
var content = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Span<byte> buffer = new byte[content.Length + 1];
var reader = BufferReader.Create(Factory.CreateWithContent(content));
// this loop skips more and more items in the reader
for (int i = 0; i < content.Length; i++) {
int copied = BufferReader.Peek(reader, buffer);
Assert.Equal(content.Length - i, copied);
Assert.True(buffer.Slice(0, copied).SequenceEqual(content.AsSpan().Slice(i)));
// make sure that nothing more got written, i.e. tail is empty
for (int r = copied; r < buffer.Length; r++)
{
Assert.Equal(0, buffer[r]);
}
reader.Skip(1);
buffer.Clear();
}
}
[Fact]
public void CopyToSmallerBufferWorks()
{
var content = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Span<byte> buffer = new byte[content.Length];
var reader = BufferReader.Create(Factory.CreateWithContent(content));
// this loop skips more and more items in the reader
for (int i = 0; i < content.Length; i++)
{
// this loop makes the destination buffer smaller and smaller
for (int j = 0; j < buffer.Length - i; j++)
{
var bufferSlice = buffer.Slice(0, j);
bufferSlice.Clear();
int copied = BufferReader.Peek(reader, bufferSlice);
Assert.Equal(Math.Min(bufferSlice.Length, content.Length - i), copied);
Assert.True(bufferSlice.Slice(0, copied).SequenceEqual(content.AsSpan().Slice(i, j)));
}
reader.Skip(1);
}
}
}
}

Просмотреть файл

@ -113,7 +113,7 @@ namespace System.IO.Pipelines.Performance.Tests
while (!reader.End)
{
var span = reader.Span;
var span = reader.CurrentSegment;
// Trim the start if we have an index
if (reader.Index > 0)