Removed Old BufferReader (#2025)
* Unified Readers * Removed PositionOf from reader * Added tests for CopyTo * reacted to changes to ReadOnlyBuffer * Renamed CopyTo to Peek
This commit is contained in:
Родитель
00bc61f856
Коммит
1fecfe7350
|
@ -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)
|
||||
|
|
Загрузка…
Ссылка в новой задаче