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

Commit

Permalink
Changed IMemoryList based on Pipelines API Review (#1982)
Browse files Browse the repository at this point in the history
  • Loading branch information
KrzysztofCwalina authored Dec 15, 2017
1 parent d3e787e commit b986281
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 106 deletions.
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
{
// 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

0 comments on commit b986281

Please sign in to comment.