From 57abf684be7c8890d447092a77e7918db52f91ad Mon Sep 17 00:00:00 2001 From: Krzysztof Cwalina Date: Thu, 11 Jan 2018 09:45:48 -0800 Subject: [PATCH] Removed Old ReadOnlyBytes (#2044) --- .../HttpServer.cs | 6 +- .../LowAllocationWebServerLibrary/Log.cs | 4 +- .../RoutingTable.cs | 6 +- .../Shared/SampleServer.cs | 10 +- .../System/Buffers/BufferExtensions.cs | 2 +- .../System/Buffers/BufferReader_search.cs | 27 +- .../System/Buffers/Sequences/ReadOnlyBytes.cs | 393 --------------- .../System/Buffers/ReadOnlyBufferReader.cs | 2 + .../System/Collections/Position.cs | 9 + src/System.Text.Http.Parser/HttpParser.cs | 21 +- tests/Benchmarks/BytesReaderBench.cs | 28 +- tests/Benchmarks/HttpParserBench.cs | 269 +--------- .../BasicUnitTests.cs | 2 +- .../BytesReaderTests.cs | 109 ++-- .../ReadOnlyBytesTests.cs | 472 ------------------ .../SequenceExtensionsTests.cs | 14 +- .../SampleCollections/LinkedContainer.cs | 10 - .../HttpParserBasicTests.cs | 7 +- .../HttpParserTests.cs | 22 +- 19 files changed, 135 insertions(+), 1278 deletions(-) delete mode 100644 src/System.Buffers.Experimental/System/Buffers/Sequences/ReadOnlyBytes.cs delete mode 100644 tests/System.Buffers.Experimental.Tests/ReadOnlyBytesTests.cs diff --git a/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/HttpServer.cs b/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/HttpServer.cs index e6ed0aabd05..3474e14cba7 100644 --- a/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/HttpServer.cs +++ b/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/HttpServer.cs @@ -80,7 +80,7 @@ protected virtual void ProcessRequest(TcpConnection socket) } } - var requestBytes = new ReadOnlyBytes(rootBuffer, requestBuffer); + var requestBytes = new ReadOnlyBuffer(rootBuffer, 0, requestBuffer, requestBuffer.Memory.Length); var request = new HttpRequest(); if(!s_parser.ParseRequestLine(ref request, requestBytes, out int consumed)) @@ -137,7 +137,7 @@ protected static void WriteCommonHeaders( formatter.Format("Date : {0:R}\r\n", DateTime.UtcNow); } - protected abstract void WriteResponse(ref HttpRequest request, ReadOnlyBytes body, TcpConnectionFormatter response); + protected abstract void WriteResponse(ref HttpRequest request, ReadOnlyBuffer body, TcpConnectionFormatter response); } public abstract class RoutingServer : HttpServer @@ -148,7 +148,7 @@ public RoutingServer(CancellationToken cancellation, Log log, ushort port, byte { } - protected override void WriteResponse(ref HttpRequest request, ReadOnlyBytes body, TcpConnectionFormatter response) + protected override void WriteResponse(ref HttpRequest request, ReadOnlyBuffer body, TcpConnectionFormatter response) { if (!Apis.TryHandle(request, body, response)) { WriteResponseFor404(ref request, response); diff --git a/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/Log.cs b/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/Log.cs index 492c73367f8..81d7b1e8dd5 100644 --- a/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/Log.cs +++ b/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/Log.cs @@ -85,7 +85,7 @@ public override void LogMessage(Level level, string message) public static class HttpLogExtensions { - public static void LogRequest(this Log log, HttpRequest request, ReadOnlyBytes body) + public static void LogRequest(this Log log, HttpRequest request, ReadOnlyBuffer body) { if (log.IsVerbose) { @@ -102,7 +102,7 @@ public static void LogRequest(this Log log, HttpRequest request, ReadOnlyBytes b } // TODO: Logger should support Utf8Span - log.LogMessage(Log.Level.Verbose, Utf8.ToString(body.Memory.Span)); + log.LogMessage(Log.Level.Verbose, Utf8.ToString(body.First.Span)); } } } diff --git a/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/RoutingTable.cs b/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/RoutingTable.cs index 5550e51c52e..89d1b5d31e4 100644 --- a/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/RoutingTable.cs +++ b/samples/LowAllocationWebServer/LowAllocationWebServerLibrary/RoutingTable.cs @@ -14,7 +14,7 @@ public class ApiRoutingTable byte[][] _uris = new byte[tablecapacity][]; TRequestId[] _requestIds = new TRequestId[tablecapacity]; Http.Method[] _verbs = new Http.Method[tablecapacity]; - Action[] _handlers = new Action[tablecapacity]; + Action[] _handlers = new Action[tablecapacity]; int _count; public TRequestId GetRequestId(HttpRequest request) @@ -25,7 +25,7 @@ public TRequestId GetRequestId(HttpRequest request) return default; } - public bool TryHandle(HttpRequest request, ReadOnlyBytes body, TcpConnectionFormatter response) + public bool TryHandle(HttpRequest request, ReadOnlyBuffer body, TcpConnectionFormatter response) { var path = new Utf8Span(request.PathBytes); for (int i = 0; i < _count; i++) @@ -40,7 +40,7 @@ public bool TryHandle(HttpRequest request, ReadOnlyBytes body, TcpConnectionForm return false; } - public void Add(Http.Method method, string requestUri, TRequestId requestId, Action handler = null) + public void Add(Http.Method method, string requestUri, TRequestId requestId, Action handler = null) { if (_count == tablecapacity) throw new NotImplementedException("ApiReoutingTable does not resize yet."); _uris[_count] = new Utf8Span(requestUri).Bytes.ToArray(); diff --git a/samples/LowAllocationWebServer/Shared/SampleServer.cs b/samples/LowAllocationWebServer/Shared/SampleServer.cs index 6ea4836a739..dc5d009ccac 100644 --- a/samples/LowAllocationWebServer/Shared/SampleServer.cs +++ b/samples/LowAllocationWebServer/Shared/SampleServer.cs @@ -34,7 +34,7 @@ public SampleRestServer(CancellationToken cancellation, Log log, ushort port, by : base(cancellation, log, port, address1, address2, address3, address4) { } - static void WriteResponseForHelloWorld(HttpRequest request, ReadOnlyBytes body, TcpConnectionFormatter response) + static void WriteResponseForHelloWorld(HttpRequest request, ReadOnlyBuffer body, TcpConnectionFormatter response) { WriteCommonHeaders(ref response, Http.Version.Http11, 200, "OK"); @@ -44,7 +44,7 @@ static void WriteResponseForHelloWorld(HttpRequest request, ReadOnlyBytes body, response.Append("Hello, World"); } - static void WriteResponseForGetTime(HttpRequest request, ReadOnlyBytes body, TcpConnectionFormatter response) + static void WriteResponseForGetTime(HttpRequest request, ReadOnlyBuffer body, TcpConnectionFormatter response) { WriteCommonHeaders(ref response, Http.Version.Http11, 200, "OK"); @@ -54,7 +54,7 @@ static void WriteResponseForGetTime(HttpRequest request, ReadOnlyBytes body, Tcp response.Format(@"Time{0:O}", DateTime.UtcNow); } - static void WriteResponseForGetJson(HttpRequest request, ReadOnlyBytes body, TcpConnectionFormatter response) + static void WriteResponseForGetJson(HttpRequest request, ReadOnlyBuffer body, TcpConnectionFormatter response) { WriteCommonHeaders(ref response, Http.Version.Http11, 200, "OK"); @@ -73,13 +73,13 @@ static void WriteResponseForGetJson(HttpRequest request, ReadOnlyBytes body, Tcp jsonWriter.WriteObjectEnd(); } - static void WriteResponseForPostJson(HttpRequest request, ReadOnlyBytes body, TcpConnectionFormatter response) + static void WriteResponseForPostJson(HttpRequest request, ReadOnlyBuffer body, TcpConnectionFormatter response) { // read request json int requestedCount; // TODO: this should not convert to span - var dom = JsonObject.Parse(body.Memory.Span); + var dom = JsonObject.Parse(body.First.Span); try { requestedCount = (int)dom["Count"]; diff --git a/src/System.Buffers.Experimental/System/Buffers/BufferExtensions.cs b/src/System.Buffers.Experimental/System/Buffers/BufferExtensions.cs index 701a6b32bf7..b8d79905be5 100644 --- a/src/System.Buffers.Experimental/System/Buffers/BufferExtensions.cs +++ b/src/System.Buffers.Experimental/System/Buffers/BufferExtensions.cs @@ -126,7 +126,7 @@ public static class BufferExtensions { const int stackLength = 32; - public static void Pipe(this IBufferOperation transformation, ReadOnlyBytes source, IOutput destination) + public static void Pipe(this IBufferOperation transformation, ReadOnlyBuffer source, IOutput destination) { int afterMergeSlice = 0; diff --git a/src/System.Buffers.Experimental/System/Buffers/BufferReader_search.cs b/src/System.Buffers.Experimental/System/Buffers/BufferReader_search.cs index 36a8b55323e..61ce2580baf 100644 --- a/src/System.Buffers.Experimental/System/Buffers/BufferReader_search.cs +++ b/src/System.Buffers.Experimental/System/Buffers/BufferReader_search.cs @@ -5,18 +5,25 @@ namespace System.Buffers { + public interface ISlicable + { + ReadOnlyBuffer Slice(Position start, Position end); + } + // TODO: the TryReadUntill methods are very inneficient. We need to fix that. public static partial class BufferReaderExtensions { - public static bool TryReadUntill(ref BufferReader reader, out ReadOnlyBytes bytes, byte delimiter) + public static bool TryReadUntill(ref BufferReader reader, out ReadOnlyBuffer bytes, byte delimiter) + where TSequence : ISequence>, ISlicable { var copy = reader; var start = reader.Position; - while (!reader.End) { + while (!reader.End) + { Position end = reader.Position; - if(reader.Take() == delimiter) + if (reader.Take() == delimiter) { - bytes = new ReadOnlyBytes(start, end); + bytes = reader.Sequence.Slice(start, end); return true; } } @@ -25,11 +32,12 @@ public static bool TryReadUntill(ref BufferReader reader, out Rea return false; } - public static bool TryReadUntill(ref BufferReader reader, out ReadOnlyBytes bytes, ReadOnlySpan delimiter) + public static bool TryReadUntill(ref BufferReader reader, out ReadOnlyBuffer bytes, ReadOnlySpan delimiter) + where TSequence : ISequence>, ISlicable { if (delimiter.Length == 0) { - bytes = ReadOnlyBytes.Empty; + bytes = default; return true; } @@ -39,7 +47,8 @@ public static bool TryReadUntill(ref BufferReader reader, out Rea var end = reader.Position; while (!reader.End) { - if (reader.Take() == delimiter[matched]) { + if (reader.Take() == delimiter[matched]) + { matched++; } else @@ -47,9 +56,9 @@ public static bool TryReadUntill(ref BufferReader reader, out Rea end = reader.Position; matched = 0; } - if(matched >= delimiter.Length) + if (matched >= delimiter.Length) { - bytes = new ReadOnlyBytes(start, end); + bytes = reader.Sequence.Slice(start, end); return true; } } diff --git a/src/System.Buffers.Experimental/System/Buffers/Sequences/ReadOnlyBytes.cs b/src/System.Buffers.Experimental/System/Buffers/Sequences/ReadOnlyBytes.cs deleted file mode 100644 index f90b045acd9..00000000000 --- a/src/System.Buffers.Experimental/System/Buffers/Sequences/ReadOnlyBytes.cs +++ /dev/null @@ -1,393 +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.Collections.Sequences; -using System.Runtime.InteropServices; -using System.Text; - -namespace System.Buffers -{ - /// - /// Multi-segment buffer - /// - public readonly struct ReadOnlyBytes : ISequence> - { - readonly object _start; - readonly int _startIndex; - readonly object _end; - readonly int _endIndex; - - public ReadOnlyBytes(byte[] bytes) - { - _start = bytes; - _startIndex = 0; - _end = bytes; - _endIndex = bytes.Length; - - Validate(); - } - - public ReadOnlyBytes(ReadOnlyMemory bytes) - { - Memory memory = MemoryMarshal.AsMemory(bytes); - if (!memory.TryGetArray(out var segment)) - { - // TODO: once we are in System.Memory, this will get OwnedMemory out of the Memory - throw new NotImplementedException(); - } - _start = segment.Array; - _startIndex = segment.Offset; - _end = segment.Array; - _endIndex = segment.Count + _startIndex; - - Validate(); - } - - // TODO: Hide this ctor and force users to pass Positions. This will let us hide Position.Object - public ReadOnlyBytes(IBufferList first, IBufferList last) - { - _start = first; - _startIndex = 0; - _end = last; - _endIndex = last.Memory.Length; - - Validate(); - } - - public ReadOnlyBytes(Position first, Position last) - { - (_start, _startIndex) = first.Get(); - (_end, _endIndex) = last.Get(); - - Validate(); - } - - private ReadOnlyBytes(byte[] bytes, int index, int length) - { - _start = bytes; - _startIndex = index; - _end = bytes; - _endIndex = length + index; - } - - private ReadOnlyBytes(object start, int startIndex, object end, int endIndex) - { - _start = start; - _startIndex = startIndex; - _end = end; - _endIndex = endIndex; - } - - public ReadOnlyBytes Slice(long index, long length) - { - if (length == 0) return Empty; - if (index == 0 && length == Length) return this; - - var kind = Kind; - switch (kind) - { - case Type.Array: - return new ReadOnlyBytes((byte[])_start, (int)(_startIndex + index), (int)length); - case Type.MemoryList: - var sl = (IBufferList)_start; - index += _startIndex; - while(true) - { - var m = sl.Memory; - if(m.Length > index) - { - break; - } - index -= m.Length; - sl = sl.Next; - } - - var el = sl; - length += index; - while (true) - { - var m = el.Memory; - if(m.Length > length) - { - return new ReadOnlyBytes(sl, (int)index, el, (int)length); - } - - length -= m.Length; - - if(length == 0) - { - return new ReadOnlyBytes(sl, (int)index, el, m.Length); - } - el = el.Next; - } - default: - throw new NotImplementedException(); - } - } - - public ReadOnlyBytes Slice(int index, int length) - => Slice((long)index, (long)length); - - public ReadOnlyBytes Slice(long index) - => Slice(index, Length - index); - - public ReadOnlyBytes Slice(int index) - => Slice((long)index); - - public ReadOnlyBytes Slice(Position position) - { - var kind = Kind; - switch (kind) - { - case Type.Array: - var (array, index) = position.Get(); - return new ReadOnlyBytes(array, index, array.Length - index); - case Type.MemoryList: - return Slice(position, new Position((IBufferList)_end, _endIndex)); - default: throw new NotImplementedException(); - } - } - - public ReadOnlyBytes Slice(Position start, Position end) - { - var kind = Kind; - switch (kind) - { - case Type.Array: - var (array, index) = start.Get(); - return new ReadOnlyBytes(array, index, (int)end - index); - case Type.MemoryList: - var (startList, startIndex) = start.Get(); - var (endList, endIndex) = end.Get(); - return new ReadOnlyBytes(startList, startIndex, endList, endIndex); - default: - throw new NotImplementedException(); - } - - } - - public static readonly ReadOnlyBytes Empty = new ReadOnlyBytes(new byte[0]); - - public ReadOnlyMemory Memory { - get { - var kind = Kind; - switch (kind) - { - case Type.Array: - return new ReadOnlyMemory((byte[])_start, _startIndex, _endIndex - _startIndex); - case Type.MemoryList: - var list = (IBufferList)_start; - if (ReferenceEquals(list, _end)) - { - return list.Memory.Slice(_startIndex, _endIndex - _startIndex); - } - else - { - return list.Memory.Slice(_startIndex); - } - default: - throw new NotImplementedException(); - } - } - } - - public long Length - { - get { - var kind = Kind; - switch (kind) - { - case Type.Array: - return _endIndex - _startIndex; - case Type.MemoryList: - var sl = (IBufferList)_start; - var el = (IBufferList)_end; - return (el.VirtualIndex + _endIndex) - (sl.VirtualIndex + _startIndex); - default: - throw new NotImplementedException(); - } - } - } - - private void Validate() - { - var kind = Kind; - switch (kind) - { - case Type.Array: - case Type.OwnedMemory: - if(!ReferenceEquals(_start, _end) || _startIndex > _endIndex) { throw new NotSupportedException(); } - break; - case Type.MemoryList: - // assume it's good? - break; - default: - throw new NotImplementedException(); - } - } - - Type Kind - { - get { - if (_start is byte[]) return Type.Array; - if (_start is OwnedMemory) return Type.OwnedMemory; - if (_start is IBufferList) return Type.MemoryList; - throw new NotSupportedException(); - } - } - - public Position Start => new Position(_start, _startIndex); - - public int CopyTo(Span buffer) - { - var array = _start as byte[]; - if (array != null) - { - int length = _endIndex - _startIndex; - if (buffer.Length < length) length = buffer.Length; - array.AsSpan().Slice(_startIndex, length).CopyTo(buffer); - return length; - } - - var position = Start; - int copied = 0; - while (TryGet(ref position, out var memory) && buffer.Length > 0) - { - var segment = memory.Span; - var length = segment.Length; - if (buffer.Length < length) length = buffer.Length; - segment.Slice(0, length).CopyTo(buffer); - buffer = buffer.Slice(length); - copied += length; - } - return copied; - } - - public Span ToSpan() - { - var array = new byte[Length]; - CopyTo(array); - return array; - } - - public bool TryGet(ref Position position, out ReadOnlyMemory item, bool advance = true) - { - if(position == default) - { - item = default; - return false; - } - - var array = _start as byte[]; - if (array != null) - { - var start = (int)position; - var length = _endIndex - (int)position; - if (length == 0) - { - item = ReadOnlyMemory.Empty; - } - else - { - item = new ReadOnlyMemory(array, start, length); - } - if (advance) position = default; - return true; - } - - if (Kind == Type.MemoryList) - { - var (node, index) = position.Get(); - item = node.Memory.Slice(index); - if (ReferenceEquals(node, _end)) - { - item = item.Slice(0, _endIndex - index); - if (advance) position = default; - } - else - { - if (advance) position = new Position(node.Next, 0); - } - return true; - } - - var om = _start as OwnedMemory; - 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(); - } - - public Position Seek(Position origin, long offset) - { - if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); - - switch (Kind) - { - case Type.Array: - { - var (array, index) = origin.Get(); - if (index + offset > array.Length) throw new ArgumentOutOfRangeException(nameof(offset)); - return new Position(array, (int)(index + offset)); - } - case Type.OwnedMemory: - { - var (om, index) = origin.Get>(); - if (index + offset > om.Length) throw new ArgumentOutOfRangeException(nameof(offset)); - return new Position(om, (int)(index + offset)); - } - case Type.MemoryList: - var previous = origin; - while (TryGet(ref origin, out var memory)) - { - var length = memory.Length; - if (length < offset) - { - offset -= length; - previous = origin; - } - else - { - var (segment, index) = previous.Get(); - return new Position(segment, (int)(index + offset)); - } - } - throw new ArgumentOutOfRangeException(nameof(offset)); - default: - throw new NotSupportedException(); - } - } - - enum Type : byte - { - Array, - OwnedMemory, - MemoryList, - } - - public override string ToString() - { - var buffer = ToSpan().ToArray(); - return Encoding.UTF8.GetString(buffer, 0, buffer.Length); - - } - } - - public static class PositionExtensions - { - public static (T segment, int index) Get(this Position position) - { - var segment = position.Segment; - var index = position.Index; - return ((T)segment, index); - } - } -} - - diff --git a/src/System.Buffers.Primitives/System/Buffers/ReadOnlyBufferReader.cs b/src/System.Buffers.Primitives/System/Buffers/ReadOnlyBufferReader.cs index 6ee4f32585d..a895c256377 100644 --- a/src/System.Buffers.Primitives/System/Buffers/ReadOnlyBufferReader.cs +++ b/src/System.Buffers.Primitives/System/Buffers/ReadOnlyBufferReader.cs @@ -46,6 +46,8 @@ public BufferReader(TSequence buffer) public int Index => _index; + public TSequence Sequence => _sequence; + public Position Position => _sequence.Seek(_currentPosition, _index); public ReadOnlySpan CurrentSegment => _currentSpan; diff --git a/src/System.Buffers.Primitives/System/Collections/Position.cs b/src/System.Buffers.Primitives/System/Collections/Position.cs index dc152ff3d39..c98ba36062b 100644 --- a/src/System.Buffers.Primitives/System/Collections/Position.cs +++ b/src/System.Buffers.Primitives/System/Collections/Position.cs @@ -1,7 +1,9 @@ // 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; using System.ComponentModel; +using System.Diagnostics; using System.Runtime.CompilerServices; namespace System.Collections.Sequences @@ -28,6 +30,13 @@ public Position(object segment) public static explicit operator int(Position position) => position._index; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public (T segment, int index) Get() + { + var segment = _segment == null ? default : (T)_segment; + return (segment, _index); + } + public static bool operator ==(Position left, Position right) => left._index == right._index && left._segment == right._segment; public static bool operator !=(Position left, Position right) => left._index != right._index || left._segment != right._segment; diff --git a/src/System.Text.Http.Parser/HttpParser.cs b/src/System.Text.Http.Parser/HttpParser.cs index 67c375c9618..90c4eb0fce2 100644 --- a/src/System.Text.Http.Parser/HttpParser.cs +++ b/src/System.Text.Http.Parser/HttpParser.cs @@ -79,10 +79,10 @@ public unsafe bool ParseRequestLine(T handler, in ReadOnlyBuffer buffer, out } static readonly byte[] s_Eol = Encoding.UTF8.GetBytes("\r\n"); - public unsafe bool ParseRequestLine(ref T handler, in ReadOnlyBytes buffer, out int consumed) where T : IHttpRequestLineHandler + public unsafe bool ParseRequestLine(ref T handler, in ReadOnlyBuffer buffer, out int consumed) where T : IHttpRequestLineHandler { // Prepare the first span - var span = buffer.Memory.Span; + var span = buffer.First.Span; var lineIndex = span.IndexOf(ByteLF); if (lineIndex >= 0) { @@ -366,7 +366,7 @@ public unsafe bool ParseHeaders(T handler, in ReadOnlyBuffer buffer, out Posi } } - public unsafe bool ParseHeaders(ref T handler, ReadOnlyBytes buffer, out int consumedBytes) where T : IHttpHeadersHandler + public unsafe bool ParseHeaders(ref T handler, ReadOnlyBuffer buffer, out int consumedBytes) where T : IHttpHeadersHandler { var index = 0; consumedBytes = 0; @@ -478,16 +478,25 @@ public unsafe bool ParseHeaders(ref T handler, ReadOnlyBytes buffer, out int } [MethodImpl(MethodImplOptions.NoInlining)] - private static void ReadTwoChars(ReadOnlyBytes buffer, int consumedBytes, out int ch1, out int ch2) + private static void ReadTwoChars(ReadOnlyBuffer buffer, int consumedBytes, out int ch1, out int ch2) { - Span temp = stackalloc byte[2]; - if (buffer.Slice(consumedBytes).CopyTo(temp) < 2) + var first = buffer.First.Span; + if(first.Length - consumedBytes > 1) + { + ch1 = first[consumedBytes]; + ch2 = first[consumedBytes + 1]; + } + + if (buffer.Length < consumedBytes + 2) { ch1 = -1; ch2 = -1; } else { + buffer = buffer.Slice(consumedBytes, 2); + Span temp = stackalloc byte[2]; + buffer.CopyTo(temp); ch1 = temp[0]; ch2 = temp[1]; } diff --git a/tests/Benchmarks/BytesReaderBench.cs b/tests/Benchmarks/BytesReaderBench.cs index bb0f7bc8ea0..09864ed317a 100644 --- a/tests/Benchmarks/BytesReaderBench.cs +++ b/tests/Benchmarks/BytesReaderBench.cs @@ -6,14 +6,12 @@ using System; using System.Buffers; using System.Buffers.Text; -using System.IO.Pipelines; using System.Text; public class BytesReaderBench { static byte[] s_data; - static ReadOnlyBytes s_readOnlyBytes; - static ReadOnlyBytes s_bytesRange; + static ReadOnlyBuffer s_buffer; static BytesReaderBench() { @@ -24,32 +22,18 @@ static BytesReaderBench() sb.Append("1234 "); } s_data = Encoding.UTF8.GetBytes(sb.ToString()); - s_readOnlyBytes = new ReadOnlyBytes(s_data); - s_bytesRange = new ReadOnlyBytes(s_data); + s_buffer = new ReadOnlyBuffer(s_data); } [Benchmark] - static void ParseInt32BytesReader() + static void ParseInt32BufferReader() { foreach (var iteration in Benchmark.Iterations) { - var reader = BufferReader.Create(s_readOnlyBytes); + var reader = BufferReader.Create(s_buffer); using (iteration.StartMeasurement()) { - while(BufferReaderExtensions.TryParse(ref reader, out int value)) { - reader.Advance(1); - } - } - } - } - [Benchmark] - static void ParseInt32BytesRangeReader() - { - foreach (var iteration in Benchmark.Iterations) { - var reader = BufferReader.Create(s_bytesRange); - - using (iteration.StartMeasurement()) { - while (BufferReaderExtensions.TryParse(ref reader, out int value)) { + while(BufferReaderExtensions.TryParse(ref reader, out int value)) { reader.Advance(1); } } @@ -75,7 +59,7 @@ static void ParseInt32Utf8Parser() } [Benchmark] - static void ParseInt32ReadableBufferReader() + static void ParseInt32BufferReaderRaw() { foreach (var iteration in Benchmark.Iterations) { diff --git a/tests/Benchmarks/HttpParserBench.cs b/tests/Benchmarks/HttpParserBench.cs index 72dacf0bbe6..ff230421219 100644 --- a/tests/Benchmarks/HttpParserBench.cs +++ b/tests/Benchmarks/HttpParserBench.cs @@ -32,7 +32,7 @@ public class HttpParserBench const int Itterations = 10000; [Benchmark(InnerIterationCount = Itterations)] - static bool RequestLineRb() + static bool RequestLine() { ReadOnlyBuffer buffer = new ReadOnlyBuffer(s_plaintextTechEmpowerRequestBytes, 0, s_plaintextTechEmpowerHeadersBytes.Length); var parser = new HttpParser(); @@ -66,73 +66,7 @@ static bool RequestLineRb() } [Benchmark(InnerIterationCount = Itterations)] - static bool RequestLineRobCursors() - { - var buffer = new ReadOnlyBytes(s_plaintextTechEmpowerRequestBytes); - var parser = new HttpParser(); - var request = new Request(); - Position consumed = default; - Position read; - bool success = true; - - foreach (var iteration in Benchmark.Iterations) - { - using (iteration.StartMeasurement()) - { - for (int i = 0; i < Benchmark.InnerIterationCount; i++) - { - success = success && parser.ParseRequestLine(ref request, buffer, out consumed, out read); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed, out read); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed, out read); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed, out read); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed, out read); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed, out read); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed, out read); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed, out read); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed, out read); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed, out read); - - } - } - } - - return success; - } - - [Benchmark(InnerIterationCount = Itterations)] - static bool RequestLineRob() - { - var buffer = new ReadOnlyBytes(s_plaintextTechEmpowerRequestBytes); - var parser = new HttpParser(); - var request = new RequestStruct(); - int consumed = 0; - bool success = true; - - foreach (var iteration in Benchmark.Iterations) - { - using (iteration.StartMeasurement()) - { - for (int i = 0; i < Benchmark.InnerIterationCount; i++) - { - success = success && parser.ParseRequestLine(ref request, buffer, out consumed); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed); - success = success && parser.ParseRequestLine(ref request, buffer, out consumed); - } - } - } - - return success; - } - - [Benchmark(InnerIterationCount = Itterations)] - static bool HeadersRb() + static bool Headers() { ReadOnlyBuffer buffer = new ReadOnlyBuffer(s_plaintextTechEmpowerHeadersBytes); var parser = new HttpParser(); @@ -163,99 +97,7 @@ static bool HeadersRb() } [Benchmark(InnerIterationCount = Itterations)] - static bool HeadersRobCursors() - { - var buffer = new ReadOnlyBytes(s_plaintextTechEmpowerHeadersBytes); - var parser = new HttpParser(); - var request = new Request(); - Position consumed = default; - Position examined; - int consumedBytes; - bool success = true; - - foreach (var iteration in Benchmark.Iterations) - { - using (iteration.StartMeasurement()) - { - for (int i = 0; i < Benchmark.InnerIterationCount; i++) - { - success = success && parser.ParseHeaders(ref request, buffer, out consumed, out examined, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer, out consumed, out examined, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer, out consumed, out examined, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer, out consumed, out examined, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer, out consumed, out examined, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer, out consumed, out examined, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer, out consumed, out examined, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer, out consumed, out examined, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer, out consumed, out examined, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer, out consumed, out examined, out consumedBytes); - } - } - } - - return success; - } - - [Benchmark(InnerIterationCount = Itterations)] - static bool HeadersRobRef() - { - var buffer = new ReadOnlyBytes(s_plaintextTechEmpowerHeadersBytes); - var parser = new HttpParser(); - var request = new RequestStruct(); - int consumed; - bool success = true; - - foreach (var iteration in Benchmark.Iterations) { - using (iteration.StartMeasurement()) { - for (int i = 0; i < Benchmark.InnerIterationCount; i++) { - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - } - } - } - - return success; - } - - [Benchmark(InnerIterationCount = Itterations)] - static bool HeadersRob() - { - var buffer = new ReadOnlyBytes(s_plaintextTechEmpowerHeadersBytes); - var parser = new HttpParser(); - var request = new Request(); - int consumed; - bool success = true; - - foreach (var iteration in Benchmark.Iterations) { - using (iteration.StartMeasurement()) { - for (int i = 0; i < Benchmark.InnerIterationCount; i++) { - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - success = success && parser.ParseHeaders(ref request, buffer, out consumed); - } - } - } - - return success; - } - - [Benchmark(InnerIterationCount = Itterations)] - static bool FullRequestRb() + static bool FullRequest() { ReadOnlyBuffer buffer = new ReadOnlyBuffer(s_plaintextTechEmpowerRequestBytes); var parser = new HttpParser(); @@ -288,111 +130,6 @@ static bool FullRequestRb() return success; } - - [Benchmark(InnerIterationCount = Itterations)] - static bool FullRequestRob() - { - var buffer = new ReadOnlyBytes(s_plaintextTechEmpowerRequestBytes); - var parser = new HttpParser(); - var request = new RequestStruct(); - int consumedBytes = 0; - bool success = true; - - foreach (var iteration in Benchmark.Iterations) { - using (iteration.StartMeasurement()) { - for (int i = 0; i < Benchmark.InnerIterationCount; i++) { - success = success && parser.ParseRequestLine(ref request, buffer, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer.Slice(consumedBytes), out consumedBytes); - success = success && parser.ParseRequestLine(ref request, buffer, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer.Slice(consumedBytes), out consumedBytes); - success = success && parser.ParseRequestLine(ref request, buffer, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer.Slice(consumedBytes), out consumedBytes); - success = success && parser.ParseRequestLine(ref request, buffer, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer.Slice(consumedBytes), out consumedBytes); - success = success && parser.ParseRequestLine(ref request, buffer, out consumedBytes); - success = success && parser.ParseHeaders(ref request, buffer.Slice(consumedBytes), out consumedBytes); - } - } - } - - return success; - } - - [Benchmark(InnerIterationCount = Itterations)] - static bool FullRequestRobCursors() - { - var buffer = new ReadOnlyBytes(s_plaintextTechEmpowerRequestBytes); - var parser = new HttpParser(); - var request = new RequestStruct(); - Position consumed = default; - Position examined; - bool success = true; - - foreach (var iteration in Benchmark.Iterations) - { - using (iteration.StartMeasurement()) - { - for (int i = 0; i < Benchmark.InnerIterationCount; i++) - { - success = success && parser.ParseRequest(ref request, buffer, out consumed, out examined); - success = success && parser.ParseRequest(ref request, buffer, out consumed, out examined); - success = success && parser.ParseRequest(ref request, buffer, out consumed, out examined); - success = success && parser.ParseRequest(ref request, buffer, out consumed, out examined); - success = success && parser.ParseRequest(ref request, buffer, out consumed, out examined); - } - } - } - - return success; - } -} - -static class HttpParserExtensions -{ - public static bool ParseRequestLine(this HttpParser parser, ref T handler, in ReadOnlyBytes buffer, out Position consumed, out Position examined) where T : IHttpRequestLineHandler - { - if(parser.ParseRequestLine(ref handler, buffer, out int consumedBytes)) - { - consumed = buffer.PositionAt(consumedBytes).GetValueOrDefault(); - examined = consumed; - return true; - } - consumed = buffer.PositionAt(0).GetValueOrDefault(); - examined = default; - return false; - } - - public static bool ParseHeaders(this HttpParser parser, ref T handler, in ReadOnlyBytes buffer, out Position consumed, out Position examined, out int consumedBytes) where T : IHttpHeadersHandler - { - if (parser.ParseHeaders(ref handler, buffer, out consumedBytes)) - { - consumed = buffer.PositionAt(consumedBytes).GetValueOrDefault(); - examined = consumed; - return true; - } - consumed = buffer.PositionAt(0).GetValueOrDefault(); - examined = default; - return false; - } - - public static bool ParseRequest(this HttpParser parser, ref T handler, in ReadOnlyBytes buffer, out Position consumed, out Position examined) where T : IHttpRequestLineHandler, IHttpHeadersHandler - { - if ( - parser.ParseRequestLine(ref handler, buffer, out var consumedRLBytes) && - parser.ParseHeaders(ref handler, buffer.Slice(consumedRLBytes), out var consumedHDBytes) - ) - { - consumed = buffer.PositionAt(consumedRLBytes + consumedHDBytes).GetValueOrDefault(); - examined = consumed; - return true; - } - else - { - consumed = buffer.PositionAt(0).GetValueOrDefault(); - examined = default; - return false; - } - } } class Request : IHttpHeadersHandler, IHttpRequestLineHandler diff --git a/tests/System.Binary.Base64.Tests/BasicUnitTests.cs b/tests/System.Binary.Base64.Tests/BasicUnitTests.cs index 67d226a70b8..b90b46e39b9 100644 --- a/tests/System.Binary.Base64.Tests/BasicUnitTests.cs +++ b/tests/System.Binary.Base64.Tests/BasicUnitTests.cs @@ -79,7 +79,7 @@ public void ValidInputOnlyMultiByte() var (first, last) = BufferList.Create(input); var output = new TestOutput(); - Base64Experimental.Utf8ToBytesDecoder.Pipe(new ReadOnlyBytes(first, last), output); + Base64Experimental.Utf8ToBytesDecoder.Pipe(new ReadOnlyBuffer(first, 0, last, last.Memory.Length), output); var expectedArray = expected.ToArray(); var array = output.GetBuffer.ToArray(); diff --git a/tests/System.Buffers.Experimental.Tests/BytesReaderTests.cs b/tests/System.Buffers.Experimental.Tests/BytesReaderTests.cs index 049ea689f04..f9e954b4e09 100644 --- a/tests/System.Buffers.Experimental.Tests/BytesReaderTests.cs +++ b/tests/System.Buffers.Experimental.Tests/BytesReaderTests.cs @@ -1,9 +1,9 @@ // 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.Generic; using System.Collections.Sequences; using System.Text; -using System.Text.Utf8; using Xunit; namespace System.Buffers.Tests @@ -13,23 +13,24 @@ public partial class BytesReaderTests [Fact] public void SingleSegmentBytesReader() { - ReadOnlyBytes bytes = Create("AB CD#EF&&"); + var bytes = new ReadOnlyBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }).AsSlicable(); var reader = BufferReader.Create(bytes); - Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var ab, (byte)' ')); - Assert.Equal("AB", ab.ToString(SymbolTable.InvariantUtf8)); + Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var ab, 3)); + Assert.True(ab.First.SequenceEqual(new byte[] { 1, 2 })); - Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var cd, (byte)'#')); - Assert.Equal("CD", cd.ToString(SymbolTable.InvariantUtf8)); + Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var cd, 6)); + Assert.True(cd.First.SequenceEqual(new byte[] { 4, 5 })); - Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var ef, new byte[] { (byte)'&', (byte)'&' })); - Assert.Equal("EF", ef.ToString(SymbolTable.InvariantUtf8)); + Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var ef, new byte[] { 8, 9 })); + Assert.True(ef.First.SequenceEqual(new byte[] { 7 })); } [Fact] public void MultiSegmentBytesReaderNumbers() { - ReadOnlyBytes bytes = ListHelper.CreateRob(new byte[][] { + + var bytes = BufferFactory.Create(new byte[][] { new byte[] { 0 }, new byte[] { 1, 2 }, new byte[] { 3, 4 }, @@ -38,7 +39,7 @@ public void MultiSegmentBytesReaderNumbers() new byte[] { 1, }, new byte[] { 0, 2, }, new byte[] { 1, 2, 3, 4 }, - }); + }).AsSlicable(); var reader = BufferReader.Create(bytes); @@ -64,38 +65,18 @@ public void MultiSegmentBytesReaderNumbers() Assert.Equal(BitConverter.ToInt32(new byte[] { 4, 3, 2, 1 }), value); } - [Fact] - public void MultiSegmentBytesReader() - { - ReadOnlyBytes bytes = Parse("A|B |CD|#EF&|&"); - var reader = BufferReader.Create(bytes); - - Assert.True(BufferReaderExtensions.TryReadUntill(ref reader, out var ab, (byte)' ')); - Assert.Equal("AB", ab.Utf8ToString()); - - 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)'&' })); - //Assert.Equal("EF", ef.ToString(SymbolTable.InvariantUtf8)); - } - [Fact] public void EmptyBytesReader() { - ReadOnlyBytes bytes = Create(""); + var bytes = ReadOnlyBuffer.Empty.AsSlicable(); var reader = BufferReader.Create(bytes); Assert.False(BufferReaderExtensions.TryReadUntill(ref reader, out var range, (byte)' ')); - - bytes = Parse("|"); - 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|"); + ReadOnlyBuffer bytes = BufferFactory.Parse("12|3Tr|ue|456Tr|ue7|89False|"); var reader = BufferReader.Create(bytes); Assert.True(BufferReaderExtensions.TryParse(ref reader, out ulong u64)); @@ -155,46 +136,48 @@ static void BytesReaderBenchmarkBaseline() } } - static class ListHelper + static class BufferFactory { - public static ReadOnlyBytes CreateRob(params byte[][] buffers) + public static ReadOnlyBuffer Create(params byte[][] buffers) { - if (buffers.Length == 1) return new ReadOnlyBytes(buffers[0]); - var (first, last) = BufferList.Create(buffers); - return new ReadOnlyBytes(first, last); + if (buffers.Length == 1) return new ReadOnlyBuffer(buffers[0]); + var list = new List>(); + foreach (var b in buffers) list.Add(b); + return new ReadOnlyBuffer(list); } - } - public static class ReadOnlyBytesTextExtensions - { - public static string ToString(this TSequence bytes, SymbolTable symbolTable) where TSequence: ISequence> + public static ReadOnlyBuffer Parse(string text) { - var sb = new StringBuilder(); - if (symbolTable == SymbolTable.InvariantUtf8) + var segments = text.Split('|'); + var buffers = new List>(); + foreach (var segment in segments) { - Position position = bytes.Start; - while (bytes.TryGet(ref position, out ReadOnlyMemory segment)) - { - sb.Append(new Utf8Span(segment.Span).ToString()); - } + buffers.Add(Encoding.UTF8.GetBytes(segment)); } - else - { - throw new NotImplementedException(); - } - return sb.ToString(); + return new ReadOnlyBuffer(buffers.ToArray()); } - public static string Utf8ToString(this TSequence bytes) where TSequence : ISequence> - { - var sb = new StringBuilder(); + public static ReadOnlyBufferToSlicableSequenceAdapter AsSlicable(this ReadOnlyBuffer buffer) + => new ReadOnlyBufferToSlicableSequenceAdapter(buffer); + } - Position position = bytes.Start; - while (bytes.TryGet(ref position, out ReadOnlyMemory segment)) - { - sb.Append(new Utf8Span(segment.Span).ToString()); - } - return sb.ToString(); + struct ReadOnlyBufferToSlicableSequenceAdapter : ISequence>, ISlicable + { + ReadOnlyBuffer _buffer; + public ReadOnlyBufferToSlicableSequenceAdapter(ReadOnlyBuffer buffer) + { + _buffer = buffer; } + + public Position Start => _buffer.Start; + + public Position Seek(Position origin, long offset) + => _buffer.Seek(origin, offset); + + public ReadOnlyBuffer Slice(Position start, Position end) + => _buffer.Slice(start, end); + + public bool TryGet(ref Position position, out ReadOnlyMemory item, bool advance = true) + => _buffer.TryGet(ref position, out item, advance); } } diff --git a/tests/System.Buffers.Experimental.Tests/ReadOnlyBytesTests.cs b/tests/System.Buffers.Experimental.Tests/ReadOnlyBytesTests.cs deleted file mode 100644 index cb18b5179ae..00000000000 --- a/tests/System.Buffers.Experimental.Tests/ReadOnlyBytesTests.cs +++ /dev/null @@ -1,472 +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.Collections.Generic; -using System.Collections.Sequences; -using System.Text; -using Xunit; - -namespace System.Buffers.Tests -{ - public partial class BytesReaderTests - { - [Fact] - public void SingleSegmentBasics() - { - var buffer = new byte[] { 1, 2, 3, 4, 5, 6 }; - var bytes = new ReadOnlyBytes(buffer); - var sliced = bytes.Slice(1, 3); - var span = sliced.Memory.Span; - Assert.Equal((byte)2, span[0]); - - Assert.Equal(buffer.Length, bytes.Length); - } - - [Fact] - public void MultiSegmentBasics() - { - var bytes = Parse("A|CD|EFG"); - bytes = bytes.Slice(2, 3); - Assert.Equal((byte)'D', bytes.Memory.Span[0]); - - bytes = Parse("A|CD|EFG"); - - Assert.Equal(6, bytes.Length); - } - - [Fact] - public void SingleSegmentSlicing() - { - var array = new byte[] { 0, 1, 2, 3, 4, 5, 6 }; - ReadOnlyMemory buffer = array; - var bytes = new ReadOnlyBytes(buffer); - - ReadOnlyBytes sliced = bytes; - ReadOnlySpan span; - for (int i = 1; i <= array.Length; i++) - { - sliced = bytes.Slice(i); - span = sliced.Memory.Span; - Assert.Equal(array.Length - i, span.Length); - if (i != array.Length) Assert.Equal(i, span[0]); - } - Assert.Equal(0, span.Length); - } - - [Fact] - public void MultiSegmentSlicing() - { - var array1 = new byte[] { 0, 1 }; - var array2 = new byte[] { 2, 3 }; - var totalLength = array1.Length + array2.Length; - ReadOnlyBytes bytes = ListHelper.CreateRob(array1, array2); - - ReadOnlyBytes sliced = bytes; - ReadOnlySpan span; - for (int i = 1; i <= totalLength; i++) - { - sliced = bytes.Slice(i); - span = sliced.Memory.Span; - Assert.Equal(totalLength - i, sliced.Length); - if (i != totalLength) Assert.Equal(i, span[0]); - } - Assert.Equal(0, span.Length); - } - - [Fact] - public void MultiSegmentPositionSlicing() - { - var array1 = new byte[] { 0, 1 }; - var array2 = new byte[] { 2, 3 }; - ReadOnlyBytes allBytes = ListHelper.CreateRob(array1, array2); - - ReadOnlyBytes allBytesSlice1 = allBytes.Slice(1); - ReadOnlyBytes allBytesSlice2 = allBytes.Slice(2); - ReadOnlyBytes allBytesSlice3 = allBytes.Slice(3); - - var positionOf3 = allBytes.PositionOf(3).GetValueOrDefault(); - var positionOf1 = allBytes.PositionOf(1).GetValueOrDefault(); - - // all bytes - { - var slice = allBytes.Slice(positionOf3); - Assert.Equal(1, slice.Length); - Assert.Equal(3, slice.Memory.Span[0]); - } - - { - var slice = allBytes.Slice(positionOf1); - Assert.Equal(3, slice.Length); - Assert.Equal(1, slice.Memory.Span[0]); - } - - // allBytesSlice1 - { - var slice = allBytesSlice1.Slice(positionOf3); - Assert.Equal(1, slice.Length); - Assert.Equal(3, slice.Memory.Span[0]); - } - - { - var slice = allBytesSlice1.Slice(positionOf1); - Assert.Equal(3, slice.Length); - Assert.Equal(1, slice.Memory.Span[0]); - } - - // allBytesSlice2 - { - var slice = allBytesSlice2.Slice(positionOf3); - Assert.Equal(1, slice.Length); - Assert.Equal(3, slice.Memory.Span[0]); - } - - { - var slice = allBytesSlice2.Slice(positionOf1); - Assert.Equal(3, slice.Length); - Assert.Equal(1, slice.Memory.Span[0]); - } - - // allBytesSlice3 - { - var slice = allBytesSlice3.Slice(positionOf3); - Assert.Equal(1, slice.Length); - Assert.Equal(3, slice.Memory.Span[0]); - } - - { - var slice = allBytesSlice3.Slice(positionOf1); - Assert.Equal(3, slice.Length); - Assert.Equal(1, slice.Memory.Span[0]); - } - } - - [Fact] - public void SingleSegmentPositionSlicing() - { - var array1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; - ReadOnlyBytes allBytes = new ReadOnlyBytes(array1); - - ReadOnlyBytes allBytesSlice = allBytes.Slice(1); - - var positionOf3 = allBytes.PositionOf(3).GetValueOrDefault(); - var positionOf1 = allBytes.PositionOf(1).GetValueOrDefault(); - - // all bytes - { - var slice = allBytes.Slice(positionOf3); - Assert.Equal(6, slice.Length); - Assert.Equal(3, slice.Memory.Span[0]); - } - - { - var slice = allBytes.Slice(positionOf1); - Assert.Equal(8, slice.Length); - Assert.Equal(1, slice.Memory.Span[0]); - } - - // allBytesSlice1 - { - var slice = allBytesSlice.Slice(positionOf3); - Assert.Equal(6, slice.Length); - Assert.Equal(3, slice.Memory.Span[0]); - } - - { - var slice = allBytesSlice.Slice(positionOf1); - Assert.Equal(8, slice.Length); - Assert.Equal(1, slice.Memory.Span[0]); - } - } - - [Fact] - public void PavelsScenarioCursorSlicing() - { - // single segment - { - var rob = ListHelper.CreateRob(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }); - var c = rob.PositionOf(5).GetValueOrDefault(); - - var slice1 = rob.Slice(2).Slice(c); - var slice2 = rob.Slice(c).Slice(2); - - Assert.NotEqual(slice1.Memory.Span[0], slice2.Memory.Span[0]); - Assert.Equal(5, slice1.Memory.Span[0]); - Assert.Equal(7, slice2.Memory.Span[0]); - } - - // multi segment - { - var rob = ListHelper.CreateRob(new byte[] { 1, 2, 3 }, new byte[] { 4, 5, 6, 7, 8 }); - var c = rob.PositionOf(5).GetValueOrDefault(); - - var slice1 = rob.Slice(2).Slice(c); - var slice2 = rob.Slice(c).Slice(2); - - Assert.NotEqual(slice1.Memory.Span[0], slice2.Memory.Span[0]); - Assert.Equal(5, slice1.Memory.Span[0]); - Assert.Equal(7, slice2.Memory.Span[0]); - } - } - - [Fact] - public void LengthPositionSlicing() - { - // single segment - { - var rob = new ReadOnlyBytes(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }); - var c2 = rob.PositionOf(2).GetValueOrDefault(); - var c5 = rob.PositionOf(5).GetValueOrDefault(); - - var slice = rob.Slice(c2, c5); - - Assert.Equal(2, slice.Memory.Span[0]); - Assert.Equal(3, slice.Length); - } - - // multi segment - { - var rob = ListHelper.CreateRob(new byte[] { 1, 2, 3 }, new byte[] { 4, 5, 6, 7, 8 }); - var c2 = rob.PositionOf(2).GetValueOrDefault(); - var c5 = rob.PositionOf(5).GetValueOrDefault(); - - var slice = rob.Slice(c2, c5); - - Assert.Equal(2, slice.Memory.Span[0]); - Assert.Equal(3, slice.Length); - } - } - - [Fact] - public void SingleSegmentCopyToKnownLength() - { - var array = new byte[] { 0, 1, 2, 3, 4, 5, 6 }; - ReadOnlyMemory buffer = array; - var bytes = new ReadOnlyBytes(buffer); - - { // copy to equal - var copy = new byte[array.Length]; - var copied = bytes.CopyTo(copy); - Assert.Equal(array.Length, copied); - Assert.Equal(array, copy); - } - - { // copy to smaller - var copy = new byte[array.Length - 1]; - var copied = bytes.CopyTo(copy); - Assert.Equal(copy.Length, copied); - for (int i = 0; i < copied; i++) - { - Assert.Equal(array[i], copy[i]); - } - } - - { // copy to larger - var copy = new byte[array.Length + 1]; - var copied = bytes.CopyTo(copy); - Assert.Equal(array.Length, copied); - for (int i = 0; i < copied; i++) - { - Assert.Equal(array[i], copy[i]); - } - } - } - - [Fact] - public void MultiSegmentCopyToUnknownLength() - { - var array1 = new byte[] { 0, 1 }; - var array2 = new byte[] { 2, 3 }; - var totalLength = array1.Length + array2.Length; - ReadOnlyBytes bytes = ListHelper.CreateRob(array1, array2); - - { // copy to equal - var copy = new byte[totalLength]; - var copied = bytes.CopyTo(copy); - Assert.Equal(totalLength, copied); - for (int i = 0; i < totalLength; i++) - { - Assert.Equal(i, copy[i]); - } - } - - { // copy to smaller - var copy = new byte[totalLength - 1]; - var copied = bytes.CopyTo(copy); - Assert.Equal(copy.Length, copied); - for (int i = 0; i < copied; i++) - { - Assert.Equal(i, copy[i]); - } - } - - { // copy to larger - var copy = new byte[totalLength + 1]; - var copied = bytes.CopyTo(copy); - Assert.Equal(totalLength, copied); - for (int i = 0; i < totalLength; i++) - { - Assert.Equal(i, copy[i]); - } - } - } - - //[Fact] - //public void MultiSegmentIndexOfSpan() - //{ - // var bytes = ListHelper.CreateRob(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, new byte[] { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }); - // Assert.Equal(10, bytes.Memory.Length); - // Assert.Equal(9, bytes.Memory.Span[9]); - - // var index = Sequence.IndexOf(bytes, new byte[] { 2, 3 }); - // Assert.Equal(2, index); - - // index = Sequence.IndexOf(bytes, new byte[] { 8, 9, 10 }); - // Assert.Equal(8, index); - - // index = Sequence.IndexOf(bytes, new byte[] { 11, 12, 13, 14 }); - // Assert.Equal(11, index); - - // index = Sequence.IndexOf(bytes, new byte[] { 19 }); - // Assert.Equal(19, index); - - // index = Sequence.IndexOf(bytes, new byte[] { 0 }); - // Assert.Equal(0, index); - - // index = Sequence.IndexOf(bytes, new byte[] { 9 }); - // Assert.Equal(9, index); - - // index = Sequence.IndexOf(bytes, new byte[] { 10 }); - // Assert.Equal(10, index); - //} - - [Fact] - public void MultiSegmentIndexOfByte() - { - var bytes = ListHelper.CreateRob(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, new byte[] { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }); - Assert.Equal(10, bytes.Memory.Length); - Assert.Equal(9, bytes.Memory.Span[9]); - - for (int i = 0; i < 20; i++) - { - var index = Sequence.IndexOf(bytes, (byte)i); - Assert.Equal(i, index); - } - } - - [Fact] - public void ReadOnlyBytesEnumeration() - { - var buffer = new byte[] { 1, 2, 3, 4, 5, 6 }; - var bytes = new ReadOnlyBytes(buffer); - Position position = bytes.Start; - int length = 0; - ReadOnlyMemory segment; - while (bytes.TryGet(ref position, out segment)) - { - length += segment.Length; - } - Assert.Equal(buffer.Length, length); - - var multibytes = Parse("A|CD|EFG"); - position = multibytes.Start; - length = 0; - while (multibytes.TryGet(ref position, out segment)) - { - length += segment.Length; - } - Assert.Equal(6, length); - } - - //[Fact] - //public void EmptyReadOnlyBytesEnumeration() - //{ - // var bytes = ReadOnlyBytes.Empty; - // { - // Position position = default; - // Assert.False(bytes.TryGet(ref position, out ReadOnlyMemory segment)); - // } - // { - // foreach (var segment in bytes) - // { - // Assert.False(true); - // } - // } - //} - - [Fact] - public void ReadOnlyTailBytesEnumeration() - { - for (int i = 0; i < 6; i++) - { - var multibytes = Parse("A|CD|EFG"); - multibytes = multibytes.Slice(i); - - { - Position position = multibytes.Start; - var length = 0; - while (multibytes.TryGet(ref position, out ReadOnlyMemory segment)) - { - length += segment.Length; - } - Assert.Equal(6 - i, length); - } - //{ - // var length = 0; - // foreach (var segment in multibytes) - // { - // length += segment.Length; - // } - // Assert.Equal(6 - i, length); - //} - } - } - - [Fact] - public void ReadOnlyFrontBytesEnumeration() - { - for (int i = 0; i < 7; i++) - { - var multibytes = Parse("A|CD|EFG"); - multibytes = multibytes.Slice(0, i); - - { - Position position = multibytes.Start; - var length = 0; - while (multibytes.TryGet(ref position, out ReadOnlyMemory segment)) - { - length += segment.Length; - } - Assert.Equal(i, length); - } - //{ - // var length = 0; - // foreach (var segment in multibytes) - // { - // length += segment.Length; - // } - // Assert.Equal(i, length); - //} - } - } - - private ReadOnlyBytes Create(params string[] segments) - { - var buffers = new List(); - foreach (var segment in segments) - { - buffers.Add(Encoding.UTF8.GetBytes(segment)); - } - return ListHelper.CreateRob(buffers.ToArray()); - } - - private ReadOnlyBytes Parse(string text) - { - var segments = text.Split('|'); - var buffers = new List(); - foreach (var segment in segments) - { - buffers.Add(Encoding.UTF8.GetBytes(segment)); - } - return ListHelper.CreateRob(buffers.ToArray()); - } - } -} diff --git a/tests/System.Buffers.Experimental.Tests/SequenceExtensionsTests.cs b/tests/System.Buffers.Experimental.Tests/SequenceExtensionsTests.cs index fd0e6b8a070..60e9f70afd2 100644 --- a/tests/System.Buffers.Experimental.Tests/SequenceExtensionsTests.cs +++ b/tests/System.Buffers.Experimental.Tests/SequenceExtensionsTests.cs @@ -10,7 +10,7 @@ public class SequenceExtensionsTests public void SequenceIndexOfSingleSegment() { var array = new byte[] { 1, 2, 3, 4, 5 }; - var bytes = new ReadOnlyBytes(array); + var bytes = new ReadOnlyBuffer(array); Assert.Equal(array.Length, bytes.Length); // Static method call to avoid calling ReadOnlyBytes.IndexOf @@ -25,7 +25,7 @@ public void SequenceIndexOfSingleSegment() [Fact] public void SequenceIndexOfMultiSegment() { - ReadOnlyBytes bytes = ListHelper.CreateRob( + ReadOnlyBuffer bytes = BufferFactory.Create( new byte[] { 1, 2}, new byte[] { 3, 4 } ); @@ -44,7 +44,7 @@ public void SequenceIndexOfMultiSegment() [Fact] public void SequenceIndexOfMultiSegmentSliced() { - ReadOnlyBytes bytes = ListHelper.CreateRob( + ReadOnlyBuffer bytes = BufferFactory.Create( new byte[] { 1, 2 }, new byte[] { 3, 4 } ); @@ -68,7 +68,7 @@ public void SequencePositionOfMultiSegment() new byte[] { 1, 2 }, new byte[] { 3, 4 } ); - var bytes = new ReadOnlyBytes(first, last); + var bytes = new ReadOnlyBuffer(first, 0, last, last.Memory.Length); Assert.Equal(4, bytes.Length); @@ -112,7 +112,7 @@ public void SequencePositionOfMultiSegment() if (listPosition != default) { robSlice = bytes.Slice(listPosition); - Assert.Equal(value, robSlice.Memory.Span[0]); + Assert.Equal(value, robSlice.First.Span[0]); } } } @@ -134,7 +134,7 @@ public void TryParseInt32Multisegment(int expected) var first = new BufferList(front); var last = first.Append(back); - var bytes = new ReadOnlyBytes(first, last); + var bytes = new ReadOnlyBuffer(first, 0, last, last.Memory.Length); Assert.True(Sequence.TryParse(bytes, out int value, out int consumed)); Assert.Equal(expected, value); @@ -143,7 +143,7 @@ public void TryParseInt32Multisegment(int expected) Assert.Equal(expected, value); var afterValue = bytes.Slice(consumedPosition); - Assert.Equal((byte)'#', afterValue.Memory.Span[0]); + Assert.Equal((byte)'#', afterValue.First.Span[0]); } } } diff --git a/tests/System.Collections.Sequences.Tests/SampleCollections/LinkedContainer.cs b/tests/System.Collections.Sequences.Tests/SampleCollections/LinkedContainer.cs index 47f9d19ffc7..f2bcca2943a 100644 --- a/tests/System.Collections.Sequences.Tests/SampleCollections/LinkedContainer.cs +++ b/tests/System.Collections.Sequences.Tests/SampleCollections/LinkedContainer.cs @@ -71,14 +71,4 @@ public Position Seek(Position origin, long offset) return new Position(node, 0); } } - - public static class PositionExtensions - { - public static (T segment, int index) Get(this Position position) - { - var segment = position.Segment; - var index = position.Index; - return ((T)segment, index); - } - } } diff --git a/tests/System.Text.Http.Parser.Tests/HttpParserBasicTests.cs b/tests/System.Text.Http.Parser.Tests/HttpParserBasicTests.cs index 95a109e4a9b..0753260d664 100644 --- a/tests/System.Text.Http.Parser.Tests/HttpParserBasicTests.cs +++ b/tests/System.Text.Http.Parser.Tests/HttpParserBasicTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Xunit; -using System.IO.Pipelines; using System.Collections.Generic; using System.Buffers; using System.Buffers.Text; @@ -17,7 +16,7 @@ public void HttpParserBasicsRob(string requestText) { var parser = new HttpParser(); var request = new Request(); - ReadOnlyBytes buffer = new ReadOnlyBytes(Encoding.ASCII.GetBytes(requestText)); + ReadOnlyBuffer buffer = new ReadOnlyBuffer(Encoding.ASCII.GetBytes(requestText)); Assert.True(parser.ParseRequestLine(ref request, buffer, out var consumed)); Assert.Equal(25, consumed); @@ -50,7 +49,7 @@ public void HttpParserSegmentedRob(string requestText) var endBytes = Encoding.ASCII.GetBytes(back); var (first, last) = BufferList.Create(frontBytes, endBytes); - ReadOnlyBytes buffer = new ReadOnlyBytes(first, last); + var buffer = new ReadOnlyBuffer(first, 0, last, last.Memory.Length); var request = new Request(); @@ -83,7 +82,7 @@ public void TechEmpowerRob() { var parser = new HttpParser(); var request = new Request(); - ReadOnlyBytes buffer = new ReadOnlyBytes(_plaintextTechEmpowerRequestBytes); + ReadOnlyBuffer buffer = new ReadOnlyBuffer(_plaintextTechEmpowerRequestBytes); Assert.True(parser.ParseRequestLine(ref request, buffer, out var consumed)); Assert.Equal(25, consumed); diff --git a/tests/System.Text.Http.Parser.Tests/HttpParserTests.cs b/tests/System.Text.Http.Parser.Tests/HttpParserTests.cs index 1f3c9365ed3..11140ba8fe7 100644 --- a/tests/System.Text.Http.Parser.Tests/HttpParserTests.cs +++ b/tests/System.Text.Http.Parser.Tests/HttpParserTests.cs @@ -26,7 +26,7 @@ public void ParsesRequestLine( string expectedVersion) { var parser = new HttpParser(); - var buffer = new ReadOnlyBytes(Encoding.ASCII.GetBytes(requestLine)); + var buffer = new ReadOnlyBuffer(Encoding.ASCII.GetBytes(requestLine)); var requestHandler = new RequestHandler(); Assert.True(parser.ParseRequestLine(ref requestHandler, buffer, out var consumed)); @@ -43,7 +43,7 @@ public void ParsesRequestLine( public void ParseRequestLineReturnsFalseWhenGivenIncompleteRequestLines(string requestLine) { var parser = new HttpParser(); - var buffer = new ReadOnlyBytes(Encoding.ASCII.GetBytes(requestLine)); + var buffer = new ReadOnlyBuffer(Encoding.ASCII.GetBytes(requestLine)); var requestHandler = new RequestHandler(); Assert.False(parser.ParseRequestLine(ref requestHandler, buffer, out var consumed)); @@ -54,7 +54,7 @@ public void ParseRequestLineReturnsFalseWhenGivenIncompleteRequestLines(string r public void ParseRequestLineDoesNotConsumeIncompleteRequestLine(string requestLine) { var parser = new HttpParser(); - var buffer = new ReadOnlyBytes(Encoding.ASCII.GetBytes(requestLine)); + var buffer = new ReadOnlyBuffer(Encoding.ASCII.GetBytes(requestLine)); var requestHandler = new RequestHandler(); Assert.False(parser.ParseRequestLine(ref requestHandler, buffer, out var consumed)); @@ -65,7 +65,7 @@ public void ParseRequestLineDoesNotConsumeIncompleteRequestLine(string requestLi public void ParseRequestLineThrowsOnInvalidRequestLine(string requestLine) { var parser = new HttpParser(); - var buffer = new ReadOnlyBytes(Encoding.ASCII.GetBytes(requestLine)); + var buffer = new ReadOnlyBuffer(Encoding.ASCII.GetBytes(requestLine)); var requestHandler = new RequestHandler(); var exception = Assert.Throws(() => @@ -82,7 +82,7 @@ public void ParseRequestLineThrowsOnNonTokenCharsInCustomMethod(string method) var requestLine = $"{method} / HTTP/1.1\r\n"; var parser = new HttpParser(); - var buffer = new ReadOnlyBytes(Encoding.ASCII.GetBytes(requestLine)); + var buffer = new ReadOnlyBuffer(Encoding.ASCII.GetBytes(requestLine)); var requestHandler = new RequestHandler(); var exception = Assert.Throws(() => @@ -99,7 +99,7 @@ public void ParseRequestLineThrowsOnUnrecognizedHttpVersion(string httpVersion) var requestLine = $"GET / {httpVersion}\r\n"; var parser = new HttpParser(); - var buffer = new ReadOnlyBytes(Encoding.ASCII.GetBytes(requestLine)); + var buffer = new ReadOnlyBuffer(Encoding.ASCII.GetBytes(requestLine)); var requestHandler = new RequestHandler(); var exception = Assert.Throws(() => @@ -150,7 +150,7 @@ public void ParseHeadersReturnsFalseWhenGivenIncompleteHeaders(string rawHeaders { var parser = new HttpParser(); - var buffer = new ReadOnlyBytes(Encoding.ASCII.GetBytes(rawHeaders)); + var buffer = new ReadOnlyBuffer(Encoding.ASCII.GetBytes(rawHeaders)); var requestHandler = new RequestHandler(); Assert.False(parser.ParseHeaders(ref requestHandler, buffer, out var consumed)); } @@ -175,7 +175,7 @@ public void ParseHeadersDoesNotConsumeIncompleteHeader(string rawHeaders) { var parser = new HttpParser(); - var buffer = new ReadOnlyBytes(Encoding.ASCII.GetBytes(rawHeaders)); + var buffer = new ReadOnlyBuffer(Encoding.ASCII.GetBytes(rawHeaders)); var requestHandler = new RequestHandler(); parser.ParseHeaders(ref requestHandler, buffer, out var consumed); @@ -263,13 +263,13 @@ public void ParseHeadersConsumesBytesCorrectlyAtEnd() var parser = new HttpParser(); const string headerLine = "Header: value\r\n\r"; - var buffer = new ReadOnlyBytes(Encoding.ASCII.GetBytes(headerLine)); + var buffer = new ReadOnlyBuffer(Encoding.ASCII.GetBytes(headerLine)); var requestHandler = new RequestHandler(); Assert.False(parser.ParseHeaders(ref requestHandler, buffer, out var consumed)); Assert.Equal(headerLine.Length - 1, consumed); - var buffer2 = new ReadOnlyBytes(Encoding.ASCII.GetBytes("\r\n")); + var buffer2 = new ReadOnlyBuffer(Encoding.ASCII.GetBytes("\r\n")); Assert.True(parser.ParseHeaders(ref requestHandler, buffer2, out consumed)); Assert.Equal(2, consumed); @@ -295,7 +295,7 @@ public void ParseHeadersThrowsOnInvalidRequestHeadersRb(string rawHeaders, strin public void ParseHeadersThrowsOnInvalidRequestHeaders(string rawHeaders, string expectedExceptionMessage) { var parser = new HttpParser(); - var buffer = new ReadOnlyBytes(Encoding.ASCII.GetBytes(rawHeaders)); + var buffer = new ReadOnlyBuffer(Encoding.ASCII.GetBytes(rawHeaders)); var requestHandler = new RequestHandler(); var exception = Assert.Throws(() =>