Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Changed IMemoryList based on Pipelines API Review #1982

Merged
merged 1 commit into from
Dec 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public BufferSequence(int desiredSize = DefaultBufferSize)

public Span<byte> Free => new Span<byte>(_array, _written, _array.Length - _written);

public IMemoryList<byte> Rest => _next;
public IMemoryList<byte> Next => _next;

public int WrittenByteCount => _written;

Expand Down
185 changes: 112 additions & 73 deletions src/System.Buffers.Experimental/System/Buffers/BufferExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,118 @@

namespace System.Buffers
{

public static class MemoryListExtensions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to use the ReadableBuffer implementation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For?

BTW, this project is not even referenced by the pipelines. I needed to add all these extension methods below to be able to compile existing code (in this project) after removing CopyTo, and ISequence implementations from IMemoryList. i.e. there is no plan to ship these members.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see

{
// span creation helpers:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long IndexOf(this IMemoryList<byte> list, ReadOnlySpan<byte> value)
{
var first = list.Memory.Span;
var index = first.IndexOf(value);
if (index != -1) return index;

var rest = list.Next;
if (rest == null) return -1;

return IndexOfStraddling(first, list.Next, value);
}

public static int CopyTo(this IMemoryList<byte> list, Span<byte> destination)
{
var current = list.Memory.Span;
int copied = 0;

while (destination.Length > 0)
{
if (current.Length >= destination.Length)
{
current.Slice(0, destination.Length).CopyTo(destination);
copied += destination.Length;
return copied;
}
else
{
current.CopyTo(destination);
copied += current.Length;
destination = destination.Slice(current.Length);
}
}
return copied;
}

public static Position PositionOf(this IMemoryList<byte> list, byte value)
{
while (list != null)
{
var current = list.Memory.Span;
var index = current.IndexOf(value);
if (index != -1) return Position.Create(list, index);
list = list.Next;
}
return Position.End;
}

// TODO (pri 3): I am pretty sure this whole routine can be written much better

// searches values that potentially straddle between first and rest
internal static long IndexOfStraddling(this ReadOnlySpan<byte> first, IMemoryList<byte> rest, ReadOnlySpan<byte> value)
{
Debug.Assert(first.IndexOf(value) == -1);
if (rest == null) return -1;

// we only need to search the end of the first buffer. More precisely, only up to value.Length - 1 bytes in the first buffer
// The other bytes in first, were already search and presumably did not match
int bytesToSkipFromFirst = 0;
if (first.Length > value.Length - 1)
{
bytesToSkipFromFirst = first.Length - value.Length - 1;
}

// now that we know how many bytes we need to skip, create slice of first buffer with bytes that need to be searched.
ReadOnlySpan<byte> bytesToSearchAgain;
if (bytesToSkipFromFirst > 0)
{
bytesToSearchAgain = first.Slice(bytesToSkipFromFirst);
}
else
{
bytesToSearchAgain = first;
}

long index;

// now combine the bytes from the end of the first buffer with bytes in the rest, and serarch the combined buffer
// this check is a small optimization: if the first byte from the value does not exist in the bytesToSearchAgain, there is no reason to combine
if (bytesToSearchAgain.IndexOf(value[0]) != -1)
{
var combinedBufferLength = value.Length << 1;
var combined = combinedBufferLength < 128 ?
stackalloc byte[combinedBufferLength] :
// TODO (pri 3): I think this could be eliminated by chunking values
new byte[combinedBufferLength];

bytesToSearchAgain.CopyTo(combined);
int combinedLength = bytesToSearchAgain.Length + rest.CopyTo(combined.Slice(bytesToSearchAgain.Length));
combined = combined.Slice(0, combinedLength);

if (combined.Length < value.Length) return -1;

index = combined.IndexOf(value);
if (index != -1)
{
return index + bytesToSkipFromFirst;
}
}

// try to find the bytes in _rest
index = rest.IndexOf(value);
if (index != -1) return first.Length + index;

return -1;
}
}

public static class BufferExtensions
{
const int stackLength = 32;
Expand Down Expand Up @@ -112,79 +224,6 @@ public static void Pipe(this IBufferOperation transformation, ReadOnlyBytes sour
return;
}

// span creation helpers:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long IndexOf(this IMemoryList<byte> sequence, ReadOnlySpan<byte> value)
{
var first = sequence.Memory.Span;
var index = first.IndexOf(value);
if (index != -1) return index;

var rest = sequence.Rest;
if (rest == null) return -1;

return IndexOfStraddling(first, sequence.Rest, value);
}

// TODO (pri 3): I am pretty sure this whole routine can be written much better

// searches values that potentially straddle between first and rest
internal static long IndexOfStraddling(this ReadOnlySpan<byte> first, IMemoryList<byte> rest, ReadOnlySpan<byte> value)
{
Debug.Assert(first.IndexOf(value) == -1);
if (rest == null) return -1;

// we only need to search the end of the first buffer. More precisely, only up to value.Length - 1 bytes in the first buffer
// The other bytes in first, were already search and presumably did not match
int bytesToSkipFromFirst = 0;
if (first.Length > value.Length - 1)
{
bytesToSkipFromFirst = first.Length - value.Length - 1;
}

// now that we know how many bytes we need to skip, create slice of first buffer with bytes that need to be searched.
ReadOnlySpan<byte> bytesToSearchAgain;
if (bytesToSkipFromFirst > 0)
{
bytesToSearchAgain = first.Slice(bytesToSkipFromFirst);
}
else
{
bytesToSearchAgain = first;
}

long index;

// now combine the bytes from the end of the first buffer with bytes in the rest, and serarch the combined buffer
// this check is a small optimization: if the first byte from the value does not exist in the bytesToSearchAgain, there is no reason to combine
if (bytesToSearchAgain.IndexOf(value[0]) != -1)
{
var combinedBufferLength = value.Length << 1;
var combined = combinedBufferLength < 128 ?
stackalloc byte[combinedBufferLength] :
// TODO (pri 3): I think this could be eliminated by chunking values
new byte[combinedBufferLength];

bytesToSearchAgain.CopyTo(combined);
int combinedLength = bytesToSearchAgain.Length + rest.CopyTo(combined.Slice(bytesToSearchAgain.Length));
combined = combined.Slice(0, combinedLength);

if (combined.Length < value.Length) return -1;

index = combined.IndexOf(value);
if (index != -1)
{
return index + bytesToSkipFromFirst;
}
}

// try to find the bytes in _rest
index = rest.IndexOf(value);
if (index != -1) return first.Length + index;

return -1;
}

public static bool TryIndicesOf(this Span<byte> buffer, byte value, Span<int> indices, out int numberOfIndices)
{
var length = buffer.Length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Sequences;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;

namespace System.Buffers
Expand Down Expand Up @@ -37,7 +35,7 @@ public MemoryList Append(Memory<byte> bytes)

public Memory<byte> Memory => _data;

public IMemoryList<byte> Rest => _next;
public IMemoryList<byte> Next => _next;

public long VirtualIndex => _virtualIndex;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public ReadOnlyBytes Slice(long index, long length)
break;
}
index -= m.Length;
sl = sl.Rest;
sl = sl.Next;
}

var el = sl;
Expand All @@ -117,7 +117,7 @@ public ReadOnlyBytes Slice(long index, long length)
{
return new ReadOnlyBytes(sl, (int)index, el, m.Length);
}
el = el.Rest;
el = el.Next;
}
default:
throw new NotImplementedException();
Expand Down Expand Up @@ -293,7 +293,7 @@ public bool TryGet(ref Position position, out ReadOnlyMemory<byte> item, bool ad
{
var first = _start as IMemoryList<byte>;
item = first.Memory.Slice(_startIndex);
if (advance) position = Position.Create(first.Rest);
if (advance) position = Position.Create(first.Next);
if (ReferenceEquals(_end, _start)){
item = item.Slice(0, (int)Length);
if (advance) position = Position.End;
Expand All @@ -310,7 +310,7 @@ public bool TryGet(ref Position position, out ReadOnlyMemory<byte> item, bool ad
}
else
{
if (advance) position = Position.Create(node.Rest);
if (advance) position = Position.Create(node.Next);
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Sequences;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace System.Buffers
{
Expand Down Expand Up @@ -97,7 +95,7 @@ public ReadWriteBytes Slice(long index, long length)
break;
}
index -= m.Length;
sl = sl.Rest;
sl = sl.Next;
}

var el = sl;
Expand All @@ -116,7 +114,7 @@ public ReadWriteBytes Slice(long index, long length)
{
return new ReadWriteBytes(sl, (int)index, el, m.Length);
}
el = el.Rest;
el = el.Next;
}
default:
throw new NotImplementedException();
Expand Down Expand Up @@ -293,7 +291,7 @@ public bool TryGet(ref Position position, out Memory<byte> item, bool advance =
{
var first = _start as IMemoryList<byte>;
item = first.Memory.Slice(_startIndex);
if (advance) position = Position.Create(first.Rest);
if (advance) position = Position.Create(first.Next);
if (ReferenceEquals(_end, _start))
{
item = item.Slice(0, (int)Length);
Expand All @@ -311,7 +309,7 @@ public bool TryGet(ref Position position, out Memory<byte> item, bool advance =
}
else
{
if (advance) position = Position.Create(node.Rest);
if (advance) position = Position.Create(node.Next);
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
// 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
namespace System.Collections.Sequences
{
public interface IMemoryList<T> : ISequence<Memory<T>>, ISequence<ReadOnlyMemory<T>>
public interface IMemoryList<T>
{
Memory<T> Memory { get; }

IMemoryList<T> Rest { get; }
IMemoryList<T> Next { get; }

long VirtualIndex { get; }

int CopyTo(Span<T> buffer);
}
}
10 changes: 1 addition & 9 deletions src/System.IO.Pipelines/System/IO/Pipelines/BufferSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Buffers;
using System.Collections.Sequences;
using System.Diagnostics;
using System.Text;

namespace System.IO.Pipelines
{
public interface IMemoryList<T>
{
Memory<T> Memory { get; }

IMemoryList<T> Next { get; }

long VirtualIndex { get; }
}

internal class BufferSegment: IMemoryList<byte>
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Buffers;
using System.Collections.Sequences;
using System.Runtime.CompilerServices;

namespace System.IO.Pipelines
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Buffers;
using System.Collections.Generic;
using System.Collections.Sequences;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;

namespace System.IO.Pipelines
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public void SequencePositionOfMultiSegment()
{
var value = (byte)(i + 1);

var listPosition = Sequence.PositionOf(first, value);
var listPosition = MemoryListExtensions.PositionOf(first, value);
var (node, index) = listPosition.Get<IMemoryList<byte>>();

if (!listPosition.IsEnd)
Expand Down