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

ReadableBuffer ctor tweaks #1227

Closed
wants to merge 9 commits into from
Closed
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
13 changes: 10 additions & 3 deletions src/System.IO.Pipelines/BufferSegment.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Buffers;
using System.Diagnostics;
using System.Text;
Expand Down Expand Up @@ -36,14 +35,21 @@ internal class BufferSegment : IDisposable
/// <summary>
/// The buffer being tracked
/// </summary>
private OwnedMemory<byte> _buffer;
private readonly OwnedMemory<byte> _buffer;

/// <summary>
/// Should never be assigned to. Cannot be made readonly directly otherwise functions Slice etc will make a defensive copy.
/// In future this may be able to be changed to a "readonly ref" and Merged with `Memory`
/// </summary>
internal Memory<byte> ReadOnlyMemory;

public BufferSegment(OwnedMemory<byte> buffer)
{
_buffer = buffer;
Start = 0;
End = 0;

ReadOnlyMemory = _buffer.Memory;
_buffer.AddReference();
}

Expand All @@ -62,10 +68,11 @@ public BufferSegment(OwnedMemory<byte> buffer, int start, int end)
_buffer = unowned.MakeCopy(start, end - start, out Start, out End);
}

ReadOnlyMemory = _buffer.Memory;
_buffer.AddReference();
}

public Memory<byte> Memory => _buffer.Memory;
public Memory<byte> Memory => ReadOnlyMemory;

/// <summary>
/// If true, data should not be written into the backing block after the End offset. Data between start and end should never be modified
Expand Down
41 changes: 41 additions & 0 deletions src/System.IO.Pipelines/EmptyByteMemory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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.Buffers;
using System.Runtime.CompilerServices;

namespace System.IO.Pipelines
{

internal class EmptyByteMemory : OwnedMemory<byte>
{
private static OwnedMemory<byte> s_ownedMemory;

public static void EnsureInitalized()
{
if (s_ownedMemory == null)
{
Initalize();
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void Initalize()
{
s_ownedMemory = new EmptyByteMemory();
ReadOnlyEmpty = s_ownedMemory.Memory;
}

/// <summary>
/// Should never be assigned to. Cannot be made readonly directly otherwise functions Slice etc will make a defensive copy.
/// In future this may be able to be changed to a "readonly ref"
/// </summary>
public static Memory<byte> ReadOnlyEmpty;

private EmptyByteMemory() : base(new byte[0], 0, 0) { }

protected override void Dispose(bool disposing)
{ }
}
}
6 changes: 3 additions & 3 deletions src/System.IO.Pipelines/MemoryEnumerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public struct MemoryEnumerator
public MemoryEnumerator(ReadCursor start, ReadCursor end)
{
_segmentEnumerator = new SegmentEnumerator(start, end);
_current = Memory<byte>.Empty;
_current = EmptyByteMemory.ReadOnlyEmpty;
}

/// <summary>
Expand All @@ -35,11 +35,11 @@ public bool MoveNext()
{
if (!_segmentEnumerator.MoveNext())
{
_current = Memory<byte>.Empty;
_current = EmptyByteMemory.ReadOnlyEmpty;
return false;
}
var current = _segmentEnumerator.Current;
_current = current.Segment.Memory.Slice(current.Start, current.Length);
_current = current.Segment.ReadOnlyMemory.Slice(current.Start, current.Length);

return true;
}
Expand Down
5 changes: 3 additions & 2 deletions src/System.IO.Pipelines/Pipe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ internal class Pipe : IPipe, IPipeReader, IPipeWriter, IReadableBufferAwaiter, I
/// <param name="options"></param>
public Pipe(IBufferPool pool, PipeOptions options = null)
{
EmptyByteMemory.EnsureInitalized();
if (pool == null)
{
throw new ArgumentNullException(nameof(pool));
Expand Down Expand Up @@ -80,7 +81,7 @@ public Pipe(IBufferPool pool, PipeOptions options = null)
_writerAwaitable = new PipeAwaitable(options.WriterScheduler ?? InlineScheduler.Default, completed: true);
}

internal Memory<byte> Memory => _writingHead?.Memory.Slice(_writingHead.End, _writingHead.WritableBytes) ?? Memory<byte>.Empty;
internal Memory<byte> Memory => _writingHead?.ReadOnlyMemory.Slice(_writingHead.End, _writingHead.WritableBytes) ?? EmptyByteMemory.ReadOnlyEmpty;

/// <summary>
/// Allocates memory from the pipeline to write into.
Expand Down Expand Up @@ -285,7 +286,7 @@ internal void AdvanceWriter(int bytesWritten)
Debug.Assert(!_writingHead.ReadOnly);
Debug.Assert(_writingHead.Next == null);

var buffer = _writingHead.Memory;
var buffer = _writingHead.ReadOnlyMemory;
var bufferIndex = _writingHead.End + bytesWritten;

Debug.Assert(bufferIndex <= buffer.Length);
Expand Down
42 changes: 15 additions & 27 deletions src/System.IO.Pipelines/ReadCursor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,40 +176,30 @@ private static void ThrowOutOfBoundsException()
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool TryGetBuffer(ReadCursor end, out Memory<byte> data, out ReadCursor cursor)
internal bool TryGetBuffer(ReadCursor end, out Memory<byte> data)
{
if (IsDefault)
{
data = Memory<byte>.Empty;
cursor = this;
return false;
}

var segment = _segment;
var index = _index;

if (end.Segment == segment)
{
var index = _index;
var following = end.Index - index;

if (following > 0)
if (segment != null && following > 0)
{
data = segment.Memory.Slice(index, following);
cursor = new ReadCursor(segment, index + following);
return true;
data = segment.ReadOnlyMemory.Slice(index, following);
}
else
{
data = EmptyByteMemory.ReadOnlyEmpty;
}

data = Memory<byte>.Empty;
cursor = this;
return false;
}
else
{
return TryGetBufferMultiBlock(end, out data, out cursor);
return !data.IsEmpty;
}

return TryGetBufferMultiBlock(end, out data);
}

private bool TryGetBufferMultiBlock(ReadCursor end, out Memory<byte> data, out ReadCursor cursor)
private bool TryGetBufferMultiBlock(ReadCursor end, out Memory<byte> data)
{
var segment = _segment;
var index = _index;
Expand Down Expand Up @@ -241,8 +231,7 @@ private bool TryGetBufferMultiBlock(ReadCursor end, out Memory<byte> data, out R

if (wasLastSegment)
{
data = Memory<byte>.Empty;
cursor = this;
data = EmptyByteMemory.ReadOnlyEmpty;
return false;
}
else
Expand All @@ -252,8 +241,7 @@ private bool TryGetBufferMultiBlock(ReadCursor end, out Memory<byte> data, out R
}
}

data = segment.Memory.Slice(index, following);
cursor = new ReadCursor(segment, index + following);
data = segment.ReadOnlyMemory.Slice(index, following);
return true;
}

Expand All @@ -265,7 +253,7 @@ public override string ToString()
}

var sb = new StringBuilder();
Span<byte> span = Segment.Memory.Span.Slice(Index, Segment.End - Index);
Span<byte> span = Segment.ReadOnlyMemory.Span.Slice(Index, Segment.End - Index);
SpanExtensions.AppendAsLiteral(span, sb);
return sb.ToString();
}
Expand Down
6 changes: 3 additions & 3 deletions src/System.IO.Pipelines/ReadCursorOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static unsafe int Seek(ReadCursor begin, ReadCursor end, out ReadCursor r
following = block.End - index;
}
ArraySegment<byte> array;
var getArrayResult = block.Memory.TryGetArray(out array);
var getArrayResult = block.ReadOnlyMemory.TryGetArray(out array);
Debug.Assert(getArrayResult);

while (following > 0)
Expand Down Expand Up @@ -142,7 +142,7 @@ public static unsafe int Seek(ReadCursor begin, ReadCursor end, out ReadCursor r
following = block.End - index;
}
ArraySegment<byte> array;
var getArrayResult = block.Memory.TryGetArray(out array);
var getArrayResult = block.ReadOnlyMemory.TryGetArray(out array);
Debug.Assert(getArrayResult);

while (following > 0)
Expand Down Expand Up @@ -249,7 +249,7 @@ public static unsafe int Seek(ReadCursor begin, ReadCursor end, out ReadCursor r
following = block.End - index;
}
ArraySegment<byte> array;
var getArrayResult = block.Memory.TryGetArray(out array);
var getArrayResult = block.ReadOnlyMemory.TryGetArray(out array);
Debug.Assert(getArrayResult);

while (following > 0)
Expand Down
34 changes: 18 additions & 16 deletions src/System.IO.Pipelines/ReadableBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,9 @@ internal ReadableBuffer(ReadCursor start, ReadCursor end)
{
_start = start;
_end = end;
if (!end.IsEnd && !end.GreaterOrEqual(start))
{
throw new ArgumentException("End should be greater or equal to start");
}
start.TryGetBuffer(end, out _first, out start);
_length = -1;

start.TryGetBuffer(end, out _first);
}

private ReadableBuffer(ref ReadableBuffer buffer)
Expand All @@ -78,10 +75,9 @@ private ReadableBuffer(ref ReadableBuffer buffer)

_start = begin;
_end = end;

_length = buffer._length;

begin.TryGetBuffer(end, out _first, out begin);
begin.TryGetBuffer(end, out _first);
}

/// <summary>
Expand Down Expand Up @@ -489,10 +485,15 @@ public static ReadableBuffer Create(byte[] data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.data);
}

return Create(data, 0, data.Length);
var segment = new BufferSegment(new OwnedArray<byte>(data))
{
Start = 0,
End = data.Length
};
return new ReadableBuffer(new ReadCursor(segment, 0), new ReadCursor(segment, data.Length));
}

/// <summary>
Expand All @@ -510,15 +511,16 @@ public static ReadableBuffer Create(byte[] data, int offset, int length)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offset);
}

if (length < 0 || (offset + length) > data.Length)
if (length < 0 || data.Length - offset < length)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
}

var buffer = new OwnedArray<byte>(data);
var segment = new BufferSegment(buffer);
segment.Start = offset;
segment.End = offset + length;
var segment = new BufferSegment(new OwnedArray<byte>(data))
{
Start = offset,
End = offset + length
};
return new ReadableBuffer(new ReadCursor(segment, offset), new ReadCursor(segment, offset + length));
}

Expand Down Expand Up @@ -558,11 +560,11 @@ bool ISequence<ReadOnlyMemory<byte>>.TryGet(ref Position position, out ReadOnlyM
}
if (currentSegment == _end.Segment)
{
item = currentSegment.Memory.Slice(currentSegment.Start, _end.Index - currentSegment.Start);
item = currentSegment.ReadOnlyMemory.Slice(currentSegment.Start, _end.Index - currentSegment.Start);
}
else
{
item = currentSegment.Memory.Slice(currentSegment.Start, currentSegment.End - currentSegment.Start);
item = currentSegment.ReadOnlyMemory.Slice(currentSegment.Start, currentSegment.End - currentSegment.Start);
}
return true;
}
Expand Down
5 changes: 5 additions & 0 deletions src/System.IO.Pipelines/ThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public static void ThrowArgumentOutOfRangeException_BufferRequestTooLarge(int ma
throw GetArgumentOutOfRangeException_BufferRequestTooLarge(maxSize);
}

internal static void ThrowArgumentException_ReadableBufferCtor()
{
throw new ArgumentException("End should be greater or equal to start");
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(ExceptionArgument argument)
{
Expand Down