diff options
author | Pavel Krymets <pavel@krymets.com> | 2018-04-10 01:03:55 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-10 01:03:55 +0300 |
commit | 1d236914207ea4ab421d05b4cee1b1edae774933 (patch) | |
tree | 0b4dd896773a4179e579ed1b89627e569c13992b | |
parent | c4d843f12060c69f3c01ec8f24146fff8a169bf4 (diff) |
ReadOnlySequence.ToString() and debug view (#28857)
7 files changed, 177 insertions, 184 deletions
diff --git a/src/System.Memory/src/System.Memory.csproj b/src/System.Memory/src/System.Memory.csproj index dda3aac4c1..e65667b647 100644 --- a/src/System.Memory/src/System.Memory.csproj +++ b/src/System.Memory/src/System.Memory.csproj @@ -33,6 +33,7 @@ <Compile Include="System\Buffers\MemoryPool.cs" /> <Compile Include="System\Buffers\OperationStatus.cs" /> <Compile Include="System\Buffers\ReadOnlySequence.cs" /> + <Compile Include="System\Buffers\ReadOnlySequenceDebugView.cs" /> <Compile Include="System\Buffers\ReadOnlySequenceSegment.cs" /> <Compile Include="System\Buffers\ReadOnlySequence_helpers.cs" /> <Compile Include="System\Buffers\Binary\Reader.cs" /> @@ -175,4 +176,4 @@ </None> </ItemGroup> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> -</Project>
\ No newline at end of file +</Project> diff --git a/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs b/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs index bdd590ddd0..58c1518d82 100644 --- a/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs +++ b/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs @@ -5,12 +5,17 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +#if !FEATURE_PORTABLE_SPAN +using Internal.Runtime.CompilerServices; +#endif // FEATURE_PORTABLE_SPAN namespace System.Buffers { /// <summary> /// Represents a sequence that can read a sequential series of <typeparam name="T" />. /// </summary> + [DebuggerTypeProxy(typeof(ReadOnlySequenceDebugView<>))] + [DebuggerDisplay("{ToString(),raw}")] public readonly partial struct ReadOnlySequence<T> { private readonly SequencePosition _sequenceStart; @@ -23,7 +28,7 @@ namespace System.Buffers public static readonly ReadOnlySequence<T> Empty = new ReadOnlySequence<T>(SpanHelpers.PerTypeValues<T>.EmptyArray); #else public static readonly ReadOnlySequence<T> Empty = new ReadOnlySequence<T>(Array.Empty<T>()); -#endif // FEATURE_PORTABLE_SPAN +#endif // FEATURE_PORTABLE_SPAN /// <summary> /// Length of the <see cref="ReadOnlySequence{T}"/>. @@ -276,7 +281,38 @@ namespace System.Buffers } /// <inheritdoc /> - public override string ToString() => string.Format("System.Buffers.ReadOnlySequence<{0}>[{1}]", typeof(T).Name, Length); + public override string ToString() + { + if (typeof(T) == typeof(char)) + { + ReadOnlySequence<T> localThis = this; + ReadOnlySequence<char> charSequence = Unsafe.As<ReadOnlySequence<T>, ReadOnlySequence<char>>(ref localThis); + + if (SequenceMarshal.TryGetString(charSequence, out string text, out int start, out int length)) + { + return text.Substring(start, length); + } + + if (Length < int.MaxValue) + { +#if !FEATURE_PORTABLE_SPAN + return string.Create((int)Length, charSequence, (span, sequence) => + { + foreach (ReadOnlyMemory<char> readOnlyMemory in sequence) + { + ReadOnlySpan<char> sourceSpan = readOnlyMemory.Span; + sourceSpan.CopyTo(span); + span = span.Slice(sourceSpan.Length); + } + }); +#else + return new string(charSequence.ToArray()); +#endif + } + } + + return string.Format("System.Buffers.ReadOnlySequence<{0}>[{1}]", typeof(T).Name, Length); + } /// <summary> /// Returns an enumerator over the <see cref="ReadOnlySequence{T}"/> diff --git a/src/System.Memory/src/System/Buffers/ReadOnlySequenceDebugView.cs b/src/System.Memory/src/System/Buffers/ReadOnlySequenceDebugView.cs new file mode 100644 index 0000000000..18eb503de4 --- /dev/null +++ b/src/System.Memory/src/System/Buffers/ReadOnlySequenceDebugView.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace System.Buffers +{ + internal sealed class ReadOnlySequenceDebugView<T> + { + private readonly T[] _array; + + private readonly ReadOnlySequenceDebugViewSegments _segments; + + public ReadOnlySequenceDebugView(ReadOnlySequence<T> sequence) + { + _array = sequence.ToArray(); + + var segmentCount = 0; + foreach (var _ in sequence) + { + segmentCount++; + } + + var segments = new ReadOnlyMemory<T>[segmentCount]; + int i = 0; + foreach (ReadOnlyMemory<T> readOnlyMemory in sequence) + { + segments[i] = readOnlyMemory; + i++; + } + _segments = new ReadOnlySequenceDebugViewSegments() + { + Segments = segments + }; + } + + public ReadOnlySequenceDebugViewSegments BufferSegments => _segments; + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items => _array; + + [DebuggerDisplay("Count: {Segments.Length}", Name = "Segments")] + public struct ReadOnlySequenceDebugViewSegments + { + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public ReadOnlyMemory<T>[] Segments { get; set; } + } + } +} diff --git a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.byte.cs b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.byte.cs index fc5c05dac1..8d4db9837f 100644 --- a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.byte.cs +++ b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.byte.cs @@ -9,87 +9,101 @@ using System.Text; namespace System.Memory.Tests { - public abstract class ReadOnlySequenceFactoryByte + public abstract class ReadOnlySequenceFactory<T> { - public static ReadOnlySequenceFactoryByte ArrayFactory { get; } = new ArrayTestSequenceFactoryByte(); - public static ReadOnlySequenceFactoryByte MemoryFactory { get; } = new MemoryTestSequenceFactoryByte(); - public static ReadOnlySequenceFactoryByte SingleSegmentFactory { get; } = new SingleSegmentTestSequenceFactoryByte(); - public static ReadOnlySequenceFactoryByte SegmentPerByteFactory { get; } = new BytePerSegmentTestSequenceFactoryByte(); + public static ReadOnlySequenceFactory<T> ArrayFactory { get; } = new ArrayTestSequenceFactory(); + public static ReadOnlySequenceFactory<T> MemoryFactory { get; } = new MemoryTestSequenceFactory(); + public static ReadOnlySequenceFactory<T> SingleSegmentFactory { get; } = new SingleSegmentTestSequenceFactory(); + public static ReadOnlySequenceFactory<T> SegmentPerItemFactory { get; } = new BytePerSegmentTestSequenceFactory(); + public static ReadOnlySequenceFactory<T> SplitInThree { get; } = new SplitInThreeSegmentsTestSequenceFactory(); - public abstract ReadOnlySequence<byte> CreateOfSize(int size); - public abstract ReadOnlySequence<byte> CreateWithContent(byte[] data); + public abstract ReadOnlySequence<T> CreateOfSize(int size); + public abstract ReadOnlySequence<T> CreateWithContent(T[] data); - public ReadOnlySequence<byte> CreateWithContent(string data) + internal class ArrayTestSequenceFactory : ReadOnlySequenceFactory<T> { - return CreateWithContent(Encoding.ASCII.GetBytes(data)); - } - - internal class ArrayTestSequenceFactoryByte : ReadOnlySequenceFactoryByte - { - public override ReadOnlySequence<byte> CreateOfSize(int size) + public override ReadOnlySequence<T> CreateOfSize(int size) { - return new ReadOnlySequence<byte>(new byte[size + 20], 10, size); + return new ReadOnlySequence<T>(new T[size + 20], 10, size); } - public override ReadOnlySequence<byte> CreateWithContent(byte[] data) + public override ReadOnlySequence<T> CreateWithContent(T[] data) { - var startSegment = new byte[data.Length + 20]; + var startSegment = new T[data.Length + 20]; Array.Copy(data, 0, startSegment, 10, data.Length); - return new ReadOnlySequence<byte>(startSegment, 10, data.Length); + return new ReadOnlySequence<T>(startSegment, 10, data.Length); } } - internal class MemoryTestSequenceFactoryByte : ReadOnlySequenceFactoryByte + internal class MemoryTestSequenceFactory : ReadOnlySequenceFactory<T> { - public override ReadOnlySequence<byte> CreateOfSize(int size) + public override ReadOnlySequence<T> CreateOfSize(int size) { - return CreateWithContent(new byte[size]); + return CreateWithContent(new T[size]); } - public override ReadOnlySequence<byte> CreateWithContent(byte[] data) + public override ReadOnlySequence<T> CreateWithContent(T[] data) { - var startSegment = new byte[data.Length + 20]; + var startSegment = new T[data.Length + 20]; Array.Copy(data, 0, startSegment, 10, data.Length); - return new ReadOnlySequence<byte>(new Memory<byte>(startSegment, 10, data.Length)); + return new ReadOnlySequence<T>(new Memory<T>(startSegment, 10, data.Length)); } } - internal class SingleSegmentTestSequenceFactoryByte : ReadOnlySequenceFactoryByte + internal class SingleSegmentTestSequenceFactory : ReadOnlySequenceFactory<T> { - public override ReadOnlySequence<byte> CreateOfSize(int size) + public override ReadOnlySequence<T> CreateOfSize(int size) { - return CreateWithContent(new byte[size]); + return CreateWithContent(new T[size]); } - public override ReadOnlySequence<byte> CreateWithContent(byte[] data) + public override ReadOnlySequence<T> CreateWithContent(T[] data) { return CreateSegments(data); } } - internal class BytePerSegmentTestSequenceFactoryByte : ReadOnlySequenceFactoryByte + internal class BytePerSegmentTestSequenceFactory : ReadOnlySequenceFactory<T> { - public override ReadOnlySequence<byte> CreateOfSize(int size) + public override ReadOnlySequence<T> CreateOfSize(int size) { - return CreateWithContent(new byte[size]); + return CreateWithContent(new T[size]); } - public override ReadOnlySequence<byte> CreateWithContent(byte[] data) + public override ReadOnlySequence<T> CreateWithContent(T[] data) { - var segments = new List<byte[]>(); + var segments = new List<T[]>(); - segments.Add(Array.Empty<byte>()); + segments.Add(Array.Empty<T>()); foreach (var b in data) { segments.Add(new[] { b }); - segments.Add(Array.Empty<byte>()); + segments.Add(Array.Empty<T>()); } return CreateSegments(segments.ToArray()); } } - public static ReadOnlySequence<byte> CreateSegments(params byte[][] inputs) + internal class SplitInThreeSegmentsTestSequenceFactory : ReadOnlySequenceFactory<T> + { + public override ReadOnlySequence<T> CreateOfSize(int size) + { + return CreateWithContent(new T[size]); + } + + public override ReadOnlySequence<T> CreateWithContent(T[] data) + { + var third = data.Length / 3; + + return CreateSegments( + data.AsSpan(0, third).ToArray(), + data.AsSpan(third, third).ToArray(), + data.AsSpan(2 * third, data.Length - 2 * third).ToArray()); + } + } + + public static ReadOnlySequence<T> CreateSegments(params T[][] inputs) { if (inputs == null || inputs.Length == 0) { @@ -98,27 +112,26 @@ namespace System.Memory.Tests int i = 0; - BufferSegment<byte> last = null; - BufferSegment<byte> first = null; + BufferSegment<T> last = null; + BufferSegment<T> first = null; do { - byte[] s = inputs[i]; + T[] s = inputs[i]; int length = s.Length; int dataOffset = length; - var chars = new byte[length * 2]; + var chars = new T[length * 2]; for (int j = 0; j < length; j++) { chars[dataOffset + j] = s[j]; } - // Create a segment that has offset relative to the OwnedMemory and OwnedMemory itself has offset relative to array - Memory<byte> memory = new Memory<byte>(chars).Slice(length, length); + Memory<T> memory = new Memory<T>(chars).Slice(length, length); if (first == null) { - first = new BufferSegment<byte>(memory); + first = new BufferSegment<T>(memory); last = first; } else @@ -128,7 +141,7 @@ namespace System.Memory.Tests i++; } while (i < inputs.Length); - return new ReadOnlySequence<byte>(first, 0, last, last.Memory.Length); + return new ReadOnlySequence<T>(first, 0, last, last.Memory.Length); } } } diff --git a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.char.cs b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.char.cs index e23f1e43d2..0002e634a5 100644 --- a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.char.cs +++ b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.char.cs @@ -12,36 +12,9 @@ namespace System.Memory.Tests { public abstract class ReadOnlySequenceFactoryChar { - public static ReadOnlySequenceFactoryChar ArrayFactory { get; } = new ArrayTestSequenceFactoryChar(); - public static ReadOnlySequenceFactoryChar MemoryFactory { get; } = new MemoryTestSequenceFactoryChar(); - public static ReadOnlySequenceFactoryChar StringFactory { get; } = new StringTestSequenceFactoryChar(); - public static ReadOnlySequenceFactoryChar SingleSegmentFactory { get; } = new SingleSegmentTestSequenceFactoryChar(); - public static ReadOnlySequenceFactoryChar SegmentPerCharFactory { get; } = new CharPerSegmentTestSequenceFactoryChar(); + public static ReadOnlySequenceFactory<char> StringFactory { get; } = new StringTestSequenceFactory(); - public abstract ReadOnlySequence<char> CreateOfSize(int size); - public abstract ReadOnlySequence<char> CreateWithContent(char[] data); - - public ReadOnlySequence<char> CreateWithContent(string data) - { - return CreateWithContent(data.ToCharArray()); - } - - internal class ArrayTestSequenceFactoryChar : ReadOnlySequenceFactoryChar - { - public override ReadOnlySequence<char> CreateOfSize(int size) - { - return new ReadOnlySequence<char>(new char[size + 20], 10, size); - } - - public override ReadOnlySequence<char> CreateWithContent(char[] data) - { - var startSegment = new char[data.Length + 20]; - Array.Copy(data, 0, startSegment, 10, data.Length); - return new ReadOnlySequence<char>(startSegment, 10, data.Length); - } - } - - internal class StringTestSequenceFactoryChar : ReadOnlySequenceFactoryChar + internal class StringTestSequenceFactory : ReadOnlySequenceFactory<char> { static string s_stringData = InitalizeStringData(); @@ -72,97 +45,5 @@ namespace System.Memory.Tests return new ReadOnlySequence<char>(text.AsMemory(10, data.Length)); } } - - internal class MemoryTestSequenceFactoryChar : ReadOnlySequenceFactoryChar - { - public override ReadOnlySequence<char> CreateOfSize(int size) - { - return CreateWithContent(new char[size]); - } - - public override ReadOnlySequence<char> CreateWithContent(char[] data) - { - var startSegment = new char[data.Length + 20]; - Array.Copy(data, 0, startSegment, 10, data.Length); - return new ReadOnlySequence<char>(new ReadOnlyMemory<char>(startSegment, 10, data.Length)); - } - } - - internal class SingleSegmentTestSequenceFactoryChar : ReadOnlySequenceFactoryChar - { - public override ReadOnlySequence<char> CreateOfSize(int size) - { - return CreateWithContent(new char[size]); - } - - public override ReadOnlySequence<char> CreateWithContent(char[] data) - { - return CreateSegments(data); - } - } - - internal class CharPerSegmentTestSequenceFactoryChar : ReadOnlySequenceFactoryChar - { - public override ReadOnlySequence<char> CreateOfSize(int size) - { - return CreateWithContent(new char[size]); - } - - public override ReadOnlySequence<char> CreateWithContent(char[] data) - { - var segments = new List<char[]>(); - - segments.Add(Array.Empty<char>()); - foreach (var b in data) - { - segments.Add(new[] { b }); - segments.Add(Array.Empty<char>()); - } - - return CreateSegments(segments.ToArray()); - } - } - - public static ReadOnlySequence<char> CreateSegments(params char[][] inputs) - { - if (inputs == null || inputs.Length == 0) - { - throw new InvalidOperationException(); - } - - int i = 0; - - BufferSegment<char> last = null; - BufferSegment<char> first = null; - - do - { - char[] s = inputs[i]; - int length = s.Length; - int dataOffset = length; - var chars = new char[length * 2]; - - for (int j = 0; j < length; j++) - { - chars[dataOffset + j] = s[j]; - } - - // Create a segment that has offset relative to the OwnedMemory and OwnedMemory itself has offset relative to array - Memory<char> memory = new Memory<char>(chars).Slice(length, length); - - if (first == null) - { - first = new BufferSegment<char>(memory); - last = first; - } - else - { - last = last.Append(memory); - } - i++; - } while (i < inputs.Length); - - return new ReadOnlySequence<char>(first, 0, last, last.Memory.Length); - } } } diff --git a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs index 42a48d8e9c..f64f7044fd 100644 --- a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs +++ b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs @@ -13,27 +13,32 @@ namespace System.Memory.Tests { public class Array : ReadOnlySequenceTestsByte { - public Array() : base(ReadOnlySequenceFactoryByte.ArrayFactory) { } + public Array() : base(ReadOnlySequenceFactory<byte>.ArrayFactory) { } } public class Memory : ReadOnlySequenceTestsByte { - public Memory() : base(ReadOnlySequenceFactoryByte.MemoryFactory) { } + public Memory() : base(ReadOnlySequenceFactory<byte>.MemoryFactory) { } } public class SingleSegment : ReadOnlySequenceTestsByte { - public SingleSegment() : base(ReadOnlySequenceFactoryByte.SingleSegmentFactory) { } + public SingleSegment() : base(ReadOnlySequenceFactory<byte>.SingleSegmentFactory) { } } public class SegmentPerByte : ReadOnlySequenceTestsByte { - public SegmentPerByte() : base(ReadOnlySequenceFactoryByte.SegmentPerByteFactory) { } + public SegmentPerByte() : base(ReadOnlySequenceFactory<byte>.SegmentPerItemFactory) { } } - internal ReadOnlySequenceFactoryByte Factory { get; } + public class SplitInThreeSegments : ReadOnlySequenceTestsByte + { + public SplitInThreeSegments() : base(ReadOnlySequenceFactory<byte>.SplitInThree) { } + } - internal ReadOnlySequenceTestsByte(ReadOnlySequenceFactoryByte factory) + internal ReadOnlySequenceFactory<byte> Factory { get; } + + internal ReadOnlySequenceTestsByte(ReadOnlySequenceFactory<byte> factory) { Factory = factory; } @@ -118,6 +123,7 @@ namespace System.Memory.Tests Assert.Throws<ArgumentOutOfRangeException>(() => buffer.GetPosition(-1, buffer.Start)); } + [Fact] public void ReadOnlyBufferSlice_ChecksEnd() { ReadOnlySequence<byte> buffer = Factory.CreateOfSize(100); @@ -249,7 +255,7 @@ namespace System.Memory.Tests [InlineData("/localhost:5000/PATH/PATH2/ HTTP/1.1", ' ', 27)] public void PositionOf_ReturnsPosition(string raw, char searchFor, int expectIndex) { - ReadOnlySequence<byte> buffer = Factory.CreateWithContent(raw); + ReadOnlySequence<byte> buffer = Factory.CreateWithContent(Encoding.ASCII.GetBytes(raw)); SequencePosition? result = buffer.PositionOf((byte)searchFor); Assert.NotNull(result); diff --git a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.char.cs b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.char.cs index 9b025fbbe8..d8de404534 100644 --- a/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.char.cs +++ b/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.char.cs @@ -13,7 +13,7 @@ namespace System.Memory.Tests { public class Array : ReadOnlySequenceTestsChar { - public Array() : base(ReadOnlySequenceFactoryChar.ArrayFactory) { } + public Array() : base(ReadOnlySequenceFactory<char>.ArrayFactory) { } } public class String : ReadOnlySequenceTestsChar @@ -23,22 +23,27 @@ namespace System.Memory.Tests public class Memory : ReadOnlySequenceTestsChar { - public Memory() : base(ReadOnlySequenceFactoryChar.MemoryFactory) { } + public Memory() : base(ReadOnlySequenceFactory<char>.MemoryFactory) { } } public class SingleSegment : ReadOnlySequenceTestsChar { - public SingleSegment() : base(ReadOnlySequenceFactoryChar.SingleSegmentFactory) { } + public SingleSegment() : base(ReadOnlySequenceFactory<char>.SingleSegmentFactory) { } } public class SegmentPerChar : ReadOnlySequenceTestsChar { - public SegmentPerChar() : base(ReadOnlySequenceFactoryChar.SegmentPerCharFactory) { } + public SegmentPerChar() : base(ReadOnlySequenceFactory<char>.SegmentPerItemFactory) { } } - internal ReadOnlySequenceFactoryChar Factory { get; } + public class SplitInThreeSegments : ReadOnlySequenceTestsChar + { + public SplitInThreeSegments() : base(ReadOnlySequenceFactory<char>.SplitInThree) { } + } + + internal ReadOnlySequenceFactory<char> Factory { get; } - internal ReadOnlySequenceTestsChar(ReadOnlySequenceFactoryChar factory) + internal ReadOnlySequenceTestsChar(ReadOnlySequenceFactory<char> factory) { Factory = factory; } @@ -74,8 +79,9 @@ namespace System.Memory.Tests [Fact] public void ToStringIsCorrect() { - ReadOnlySequence<char> buffer = Factory.CreateWithContent(Enumerable.Range(0, 255).Select(i => (char)i).ToArray()); - Assert.Equal("System.Buffers.ReadOnlySequence<Char>[255]", buffer.ToString()); + char[] array = Enumerable.Range(0, 255).Select(i => (char)i).ToArray(); + ReadOnlySequence<char> buffer = Factory.CreateWithContent(array); + Assert.Equal(array, buffer.ToString()); } [Theory] @@ -252,7 +258,7 @@ namespace System.Memory.Tests [InlineData("/localhost:5000/PATH/PATH2/ HTTP/1.1", ' ', 27)] public void PositionOf_ReturnsPosition(string raw, char searchFor, int expectIndex) { - ReadOnlySequence<char> buffer = Factory.CreateWithContent(raw); + ReadOnlySequence<char> buffer = Factory.CreateWithContent(raw.ToCharArray()); SequencePosition? result = buffer.PositionOf((char)searchFor); Assert.NotNull(result); |