You've already forked linux-packaging-mono
Imported Upstream version 6.6.0.103
Former-commit-id: ed8637df0061cb6f128ae1d26d26db7734e0fa0d
This commit is contained in:
parent
53ae335b9c
commit
9b1ef54dc9
185
mcs/class/corlib/corefx/ArrayBufferWriter.cs
Normal file
185
mcs/class/corlib/corefx/ArrayBufferWriter.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a heap-based, array-backed output sink into which <typeparam name="T"/> data can be written.
|
||||
/// </summary>
|
||||
public sealed class ArrayBufferWriter<T> : IBufferWriter<T>
|
||||
{
|
||||
private T[] _buffer;
|
||||
private int _index;
|
||||
|
||||
private const int DefaultInitialBufferSize = 256;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to,
|
||||
/// with the default initial capacity.
|
||||
/// </summary>
|
||||
public ArrayBufferWriter()
|
||||
{
|
||||
_buffer = Array.Empty<T>();
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to,
|
||||
/// with an initial capacity specified.
|
||||
/// </summary>
|
||||
/// <param name="initialCapacity">The minimum capacity with which to initialize the underlying buffer.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="initialCapacity"/> is not positive (i.e. less than or equal to 0).
|
||||
/// </exception>
|
||||
public ArrayBufferWriter(int initialCapacity)
|
||||
{
|
||||
if (initialCapacity <= 0)
|
||||
throw new ArgumentException(nameof(initialCapacity));
|
||||
|
||||
_buffer = new T[initialCapacity];
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlyMemory{T}"/>.
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<T> WrittenMemory => _buffer.AsMemory(0, _index);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlySpan{T}"/>.
|
||||
/// </summary>
|
||||
public ReadOnlySpan<T> WrittenSpan => _buffer.AsSpan(0, _index);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the amount of data written to the underlying buffer so far.
|
||||
/// </summary>
|
||||
public int WrittenCount => _index;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total amount of space within the underlying buffer.
|
||||
/// </summary>
|
||||
public int Capacity => _buffer.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the amount of space available that can still be written into without forcing the underlying buffer to grow.
|
||||
/// </summary>
|
||||
public int FreeCapacity => _buffer.Length - _index;
|
||||
|
||||
/// <summary>
|
||||
/// Clears the data written to the underlying buffer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You must clear the <see cref="ArrayBufferWriter{T}"/> before trying to re-use it.
|
||||
/// </remarks>
|
||||
public void Clear()
|
||||
{
|
||||
Debug.Assert(_buffer.Length >= _index);
|
||||
_buffer.AsSpan(0, _index).Clear();
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies <see cref="IBufferWriter{T}"/> that <paramref name="count"/> amount of data was written to the output <see cref="Span{T}"/>/<see cref="Memory{T}"/>
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="count"/> is negative.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when attempting to advance past the end of the underlying buffer.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
|
||||
/// </remarks>
|
||||
public void Advance(int count)
|
||||
{
|
||||
if (count < 0)
|
||||
throw new ArgumentException(nameof(count));
|
||||
|
||||
if (_index > _buffer.Length - count)
|
||||
ThrowInvalidOperationException_AdvancedTooFar(_buffer.Length);
|
||||
|
||||
_index += count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Memory{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>).
|
||||
/// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="sizeHint"/> is negative.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// This will never return an empty <see cref="Memory{T}"/>.
|
||||
/// </remarks>
|
||||
/// <remarks>
|
||||
/// There is no guarantee that successive calls will return the same buffer or the same-sized buffer.
|
||||
/// </remarks>
|
||||
/// <remarks>
|
||||
/// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
|
||||
/// </remarks>
|
||||
public Memory<T> GetMemory(int sizeHint = 0)
|
||||
{
|
||||
CheckAndResizeBuffer(sizeHint);
|
||||
Debug.Assert(_buffer.Length > _index);
|
||||
return _buffer.AsMemory(_index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Span{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>).
|
||||
/// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="sizeHint"/> is negative.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// This will never return an empty <see cref="Span{T}"/>.
|
||||
/// </remarks>
|
||||
/// <remarks>
|
||||
/// There is no guarantee that successive calls will return the same buffer or the same-sized buffer.
|
||||
/// </remarks>
|
||||
/// <remarks>
|
||||
/// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
|
||||
/// </remarks>
|
||||
public Span<T> GetSpan(int sizeHint = 0)
|
||||
{
|
||||
CheckAndResizeBuffer(sizeHint);
|
||||
Debug.Assert(_buffer.Length > _index);
|
||||
return _buffer.AsSpan(_index);
|
||||
}
|
||||
|
||||
private void CheckAndResizeBuffer(int sizeHint)
|
||||
{
|
||||
if (sizeHint < 0)
|
||||
throw new ArgumentException(nameof(sizeHint));
|
||||
|
||||
if (sizeHint == 0)
|
||||
{
|
||||
sizeHint = 1;
|
||||
}
|
||||
|
||||
if (sizeHint > FreeCapacity)
|
||||
{
|
||||
int growBy = Math.Max(sizeHint, _buffer.Length);
|
||||
|
||||
if (_buffer.Length == 0)
|
||||
{
|
||||
growBy = Math.Max(growBy, DefaultInitialBufferSize);
|
||||
}
|
||||
|
||||
int newSize = checked(_buffer.Length + growBy);
|
||||
|
||||
Array.Resize(ref _buffer, newSize);
|
||||
}
|
||||
|
||||
Debug.Assert(FreeCapacity > 0 && FreeCapacity >= sizeHint);
|
||||
}
|
||||
|
||||
private static void ThrowInvalidOperationException_AdvancedTooFar(int capacity)
|
||||
{
|
||||
throw new InvalidOperationException(SR.Format(SR.BufferWriterAdvancedTooFar, capacity));
|
||||
}
|
||||
}
|
||||
}
|
128
mcs/class/corlib/corefx/NullableAttributes.cs
Normal file
128
mcs/class/corlib/corefx/NullableAttributes.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
// 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.
|
||||
|
||||
namespace System.Diagnostics.CodeAnalysis
|
||||
{
|
||||
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
|
||||
#if INTERNAL_NULLABLE_ATTRIBUTES
|
||||
internal
|
||||
#else
|
||||
public
|
||||
#endif
|
||||
sealed class AllowNullAttribute : Attribute { }
|
||||
|
||||
/// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
|
||||
#if INTERNAL_NULLABLE_ATTRIBUTES
|
||||
internal
|
||||
#else
|
||||
public
|
||||
#endif
|
||||
sealed class DisallowNullAttribute : Attribute { }
|
||||
|
||||
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
|
||||
#if INTERNAL_NULLABLE_ATTRIBUTES
|
||||
internal
|
||||
#else
|
||||
public
|
||||
#endif
|
||||
sealed class MaybeNullAttribute : Attribute { }
|
||||
|
||||
/// <summary>Specifies that an output will not be null even if the corresponding type allows it.</summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
|
||||
#if INTERNAL_NULLABLE_ATTRIBUTES
|
||||
internal
|
||||
#else
|
||||
public
|
||||
#endif
|
||||
sealed class NotNullAttribute : Attribute { }
|
||||
|
||||
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding type disallows it.</summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
|
||||
#if INTERNAL_NULLABLE_ATTRIBUTES
|
||||
internal
|
||||
#else
|
||||
public
|
||||
#endif
|
||||
sealed class MaybeNullWhenAttribute : Attribute
|
||||
{
|
||||
/// <summary>Initializes the attribute with the specified return value condition.</summary>
|
||||
/// <param name="returnValue">
|
||||
/// The return value condition. If the method returns this value, the associated parameter may be null.
|
||||
/// </param>
|
||||
public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
|
||||
|
||||
/// <summary>Gets the return value condition.</summary>
|
||||
public bool ReturnValue { get; }
|
||||
}
|
||||
|
||||
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
|
||||
#if INTERNAL_NULLABLE_ATTRIBUTES
|
||||
internal
|
||||
#else
|
||||
public
|
||||
#endif
|
||||
sealed class NotNullWhenAttribute : Attribute
|
||||
{
|
||||
/// <summary>Initializes the attribute with the specified return value condition.</summary>
|
||||
/// <param name="returnValue">
|
||||
/// The return value condition. If the method returns this value, the associated parameter will not be null.
|
||||
/// </param>
|
||||
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
|
||||
|
||||
/// <summary>Gets the return value condition.</summary>
|
||||
public bool ReturnValue { get; }
|
||||
}
|
||||
|
||||
/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
|
||||
#if INTERNAL_NULLABLE_ATTRIBUTES
|
||||
internal
|
||||
#else
|
||||
public
|
||||
#endif
|
||||
sealed class NotNullIfNotNullAttribute : Attribute
|
||||
{
|
||||
/// <summary>Initializes the attribute with the associated parameter name.</summary>
|
||||
/// <param name="parameterName">
|
||||
/// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
|
||||
/// </param>
|
||||
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
|
||||
|
||||
/// <summary>Gets the associated parameter name.</summary>
|
||||
public string ParameterName { get; }
|
||||
}
|
||||
|
||||
/// <summary>Applied to a method that will never return under any circumstance.</summary>
|
||||
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
|
||||
#if INTERNAL_NULLABLE_ATTRIBUTES
|
||||
internal
|
||||
#else
|
||||
public
|
||||
#endif
|
||||
sealed class DoesNotReturnAttribute : Attribute { }
|
||||
|
||||
/// <summary>Specifies that the method will not return if the associated Boolean parameter is passed the specified value.</summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
|
||||
#if INTERNAL_NULLABLE_ATTRIBUTES
|
||||
internal
|
||||
#else
|
||||
public
|
||||
#endif
|
||||
sealed class DoesNotReturnIfAttribute : Attribute
|
||||
{
|
||||
/// <summary>Initializes the attribute with the specified parameter value.</summary>
|
||||
/// <param name="parameterValue">
|
||||
/// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to
|
||||
/// the associated parameter matches this value.
|
||||
/// </param>
|
||||
public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;
|
||||
|
||||
/// <summary>Gets the condition parameter value.</summary>
|
||||
public bool ParameterValue { get; }
|
||||
}
|
||||
}
|
@@ -26,4 +26,5 @@ partial class SR
|
||||
public const string Arg_SwitchExpressionException = "Non-exhaustive switch expression failed to match its input.";
|
||||
public const string SwitchExpressionException_UnmatchedValue = "Unmatched value was {0}.";
|
||||
public const string Argument_InvalidRandomRange = "Range of random number does not contain at least one possibility.";
|
||||
public const string BufferWriterAdvancedTooFar = "Cannot advance past the end of the buffer, which has a size of {0}.";
|
||||
}
|
778
mcs/class/corlib/corefx/SequenceReader.Search.cs
Normal file
778
mcs/class/corlib/corefx/SequenceReader.Search.cs
Normal file
File diff suppressed because it is too large
Load Diff
449
mcs/class/corlib/corefx/SequenceReader.cs
Normal file
449
mcs/class/corlib/corefx/SequenceReader.cs
Normal file
@@ -0,0 +1,449 @@
|
||||
// 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;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using Internal.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
#if __MonoCS__
|
||||
public ref partial struct SequenceReader<T> where T : IEquatable<T>
|
||||
#else
|
||||
public ref partial struct SequenceReader<T> where T : unmanaged, IEquatable<T>
|
||||
#endif
|
||||
{
|
||||
private SequencePosition _currentPosition;
|
||||
private SequencePosition _nextPosition;
|
||||
private bool _moreData;
|
||||
#if __MonoCS__
|
||||
private long _length;
|
||||
#else
|
||||
private readonly long _length;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="SequenceReader{T}"/> over the given <see cref="ReadOnlySequence{T}"/>.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public SequenceReader(ReadOnlySequence<T> sequence)
|
||||
{
|
||||
CurrentSpanIndex = 0;
|
||||
Consumed = 0;
|
||||
Sequence = sequence;
|
||||
_currentPosition = sequence.Start;
|
||||
_length = -1;
|
||||
|
||||
sequence.GetFirstSpan(out ReadOnlySpan<T> first, out _nextPosition);
|
||||
CurrentSpan = first;
|
||||
_moreData = first.Length > 0;
|
||||
|
||||
if (!_moreData && !sequence.IsSingleSegment)
|
||||
{
|
||||
_moreData = true;
|
||||
GetNextSpan();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True when there is no more data in the <see cref="Sequence"/>.
|
||||
/// </summary>
|
||||
#if __MonoCS__
|
||||
public bool End => !_moreData;
|
||||
#else
|
||||
public readonly bool End => !_moreData;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The underlying <see cref="ReadOnlySequence{T}"/> for the reader.
|
||||
/// </summary>
|
||||
#if __MonoCS__
|
||||
public ReadOnlySequence<T> Sequence { get; }
|
||||
#else
|
||||
public readonly ReadOnlySequence<T> Sequence { get; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The current position in the <see cref="Sequence"/>.
|
||||
/// </summary>
|
||||
#if __MonoCS__
|
||||
public SequencePosition Position
|
||||
=> Sequence.GetPosition(CurrentSpanIndex, _currentPosition);
|
||||
#else
|
||||
public readonly SequencePosition Position
|
||||
=> Sequence.GetPosition(CurrentSpanIndex, _currentPosition);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The current segment in the <see cref="Sequence"/> as a span.
|
||||
/// </summary>
|
||||
#if __MonoCS__
|
||||
public ReadOnlySpan<T> CurrentSpan { get; private set; }
|
||||
#else
|
||||
public ReadOnlySpan<T> CurrentSpan { readonly get; private set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The index in the <see cref="CurrentSpan"/>.
|
||||
/// </summary>
|
||||
#if __MonoCS__
|
||||
public int CurrentSpanIndex { get; private set; }
|
||||
#else
|
||||
public int CurrentSpanIndex { readonly get; private set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The unread portion of the <see cref="CurrentSpan"/>.
|
||||
/// </summary>
|
||||
#if __MonoCS__
|
||||
public ReadOnlySpan<T> UnreadSpan
|
||||
#else
|
||||
public readonly ReadOnlySpan<T> UnreadSpan
|
||||
#endif
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => CurrentSpan.Slice(CurrentSpanIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The total number of <typeparamref name="T"/>'s processed by the reader.
|
||||
/// </summary>
|
||||
#if __MonoCS__
|
||||
public long Consumed { get; private set; }
|
||||
#else
|
||||
public long Consumed { readonly get; private set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Remaining <typeparamref name="T"/>'s in the reader's <see cref="Sequence"/>.
|
||||
/// </summary>
|
||||
#if __MonoCS__
|
||||
public long Remaining => Length - Consumed;
|
||||
#else
|
||||
public readonly long Remaining => Length - Consumed;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Count of <typeparamref name="T"/> in the reader's <see cref="Sequence"/>.
|
||||
/// </summary>
|
||||
#if __MonoCS__
|
||||
public long Length
|
||||
#else
|
||||
public readonly long Length
|
||||
#endif
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_length < 0)
|
||||
{
|
||||
unsafe {
|
||||
fixed (long* lenPtr = &_length)
|
||||
// Cast-away readonly to initialize lazy field
|
||||
Volatile.Write(ref Unsafe.AsRef<long>(lenPtr), Sequence.Length);
|
||||
}
|
||||
}
|
||||
return _length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Peeks at the next value without advancing the reader.
|
||||
/// </summary>
|
||||
/// <param name="value">The next value or default if at the end.</param>
|
||||
/// <returns>False if at the end of the reader.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#if __MonoCS__
|
||||
public bool TryPeek(out T value)
|
||||
#else
|
||||
public readonly bool TryPeek(out T value)
|
||||
#endif
|
||||
{
|
||||
if (_moreData)
|
||||
{
|
||||
value = CurrentSpan[CurrentSpanIndex];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the next value and advance the reader.
|
||||
/// </summary>
|
||||
/// <param name="value">The next value or default if at the end.</param>
|
||||
/// <returns>False if at the end of the reader.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryRead(out T value)
|
||||
{
|
||||
if (End)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = CurrentSpan[CurrentSpanIndex];
|
||||
CurrentSpanIndex++;
|
||||
Consumed++;
|
||||
|
||||
if (CurrentSpanIndex >= CurrentSpan.Length)
|
||||
{
|
||||
GetNextSpan();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the reader back the specified number of items.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown if trying to rewind a negative amount or more than <see cref="Consumed"/>.
|
||||
/// </exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Rewind(long count)
|
||||
{
|
||||
if ((ulong)count > (ulong)Consumed)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count);
|
||||
}
|
||||
|
||||
Consumed -= count;
|
||||
|
||||
if (CurrentSpanIndex >= count)
|
||||
{
|
||||
CurrentSpanIndex -= (int)count;
|
||||
_moreData = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Current segment doesn't have enough data, scan backward through segments
|
||||
RetreatToPreviousSpan(Consumed);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void RetreatToPreviousSpan(long consumed)
|
||||
{
|
||||
ResetReader();
|
||||
Advance(consumed);
|
||||
}
|
||||
|
||||
private void ResetReader()
|
||||
{
|
||||
CurrentSpanIndex = 0;
|
||||
Consumed = 0;
|
||||
_currentPosition = Sequence.Start;
|
||||
_nextPosition = _currentPosition;
|
||||
|
||||
if (Sequence.TryGet(ref _nextPosition, out ReadOnlyMemory<T> memory, advance: true))
|
||||
{
|
||||
_moreData = true;
|
||||
|
||||
if (memory.Length == 0)
|
||||
{
|
||||
CurrentSpan = default;
|
||||
// No data in the first span, move to one with data
|
||||
GetNextSpan();
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentSpan = memory.Span;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No data in any spans and at end of sequence
|
||||
_moreData = false;
|
||||
CurrentSpan = default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the next segment with available data, if any.
|
||||
/// </summary>
|
||||
private void GetNextSpan()
|
||||
{
|
||||
if (!Sequence.IsSingleSegment)
|
||||
{
|
||||
SequencePosition previousNextPosition = _nextPosition;
|
||||
while (Sequence.TryGet(ref _nextPosition, out ReadOnlyMemory<T> memory, advance: true))
|
||||
{
|
||||
_currentPosition = previousNextPosition;
|
||||
if (memory.Length > 0)
|
||||
{
|
||||
CurrentSpan = memory.Span;
|
||||
CurrentSpanIndex = 0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentSpan = default;
|
||||
CurrentSpanIndex = 0;
|
||||
previousNextPosition = _nextPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
_moreData = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the reader ahead the specified number of items.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Advance(long count)
|
||||
{
|
||||
const long TooBigOrNegative = unchecked((long)0xFFFFFFFF80000000);
|
||||
if ((count & TooBigOrNegative) == 0 && CurrentSpan.Length - CurrentSpanIndex > (int)count)
|
||||
{
|
||||
CurrentSpanIndex += (int)count;
|
||||
Consumed += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Can't satisfy from the current span
|
||||
AdvanceToNextSpan(count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unchecked helper to avoid unnecessary checks where you know count is valid.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void AdvanceCurrentSpan(long count)
|
||||
{
|
||||
Debug.Assert(count >= 0);
|
||||
|
||||
Consumed += count;
|
||||
CurrentSpanIndex += (int)count;
|
||||
if (CurrentSpanIndex >= CurrentSpan.Length)
|
||||
GetNextSpan();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Only call this helper if you know that you are advancing in the current span
|
||||
/// with valid count and there is no need to fetch the next one.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void AdvanceWithinSpan(long count)
|
||||
{
|
||||
Debug.Assert(count >= 0);
|
||||
|
||||
Consumed += count;
|
||||
CurrentSpanIndex += (int)count;
|
||||
|
||||
Debug.Assert(CurrentSpanIndex < CurrentSpan.Length);
|
||||
}
|
||||
|
||||
private void AdvanceToNextSpan(long count)
|
||||
{
|
||||
if (count < 0)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count);
|
||||
}
|
||||
|
||||
Consumed += count;
|
||||
while (_moreData)
|
||||
{
|
||||
int remaining = CurrentSpan.Length - CurrentSpanIndex;
|
||||
|
||||
if (remaining > count)
|
||||
{
|
||||
CurrentSpanIndex += (int)count;
|
||||
count = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// As there may not be any further segments we need to
|
||||
// push the current index to the end of the span.
|
||||
CurrentSpanIndex += remaining;
|
||||
count -= remaining;
|
||||
Debug.Assert(count >= 0);
|
||||
|
||||
GetNextSpan();
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count != 0)
|
||||
{
|
||||
// Not enough data left- adjust for where we actually ended and throw
|
||||
Consumed -= count;
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies data from the current <see cref="Position"/> to the given <paramref name="destination"/> span if there
|
||||
/// is enough data to fill it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This API is used to copy a fixed amount of data out of the sequence if possible. It does not advance
|
||||
/// the reader. To look ahead for a specific stream of data <see cref="IsNext(ReadOnlySpan{T}, bool)"/> can be used.
|
||||
/// </remarks>
|
||||
/// <param name="destination">Destination span to copy to.</param>
|
||||
/// <returns>True if there is enough data to completely fill the <paramref name="destination"/> span.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#if __MonoCS__
|
||||
public bool TryCopyTo(Span<T> destination)
|
||||
#else
|
||||
public readonly bool TryCopyTo(Span<T> destination)
|
||||
#endif
|
||||
{
|
||||
// This API doesn't advance to facilitate conditional advancement based on the data returned.
|
||||
// We don't provide an advance option to allow easier utilizing of stack allocated destination spans.
|
||||
// (Because we can make this method readonly we can guarantee that we won't capture the span.)
|
||||
|
||||
ReadOnlySpan<T> firstSpan = UnreadSpan;
|
||||
if (firstSpan.Length >= destination.Length)
|
||||
{
|
||||
firstSpan.Slice(0, destination.Length).CopyTo(destination);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not enough in the current span to satisfy the request, fall through to the slow path
|
||||
return TryCopyMultisegment(destination);
|
||||
}
|
||||
|
||||
#if __MonoCS__
|
||||
internal bool TryCopyMultisegment(Span<T> destination)
|
||||
#else
|
||||
internal readonly bool TryCopyMultisegment(Span<T> destination)
|
||||
#endif
|
||||
{
|
||||
// If we don't have enough to fill the requested buffer, return false
|
||||
if (Remaining < destination.Length)
|
||||
return false;
|
||||
|
||||
ReadOnlySpan<T> firstSpan = UnreadSpan;
|
||||
Debug.Assert(firstSpan.Length < destination.Length);
|
||||
firstSpan.CopyTo(destination);
|
||||
int copied = firstSpan.Length;
|
||||
|
||||
SequencePosition next = _nextPosition;
|
||||
while (Sequence.TryGet(ref next, out ReadOnlyMemory<T> nextSegment, true))
|
||||
{
|
||||
if (nextSegment.Length > 0)
|
||||
{
|
||||
ReadOnlySpan<T> nextSpan = nextSegment.Span;
|
||||
int toCopy = Math.Min(nextSpan.Length, destination.Length - copied);
|
||||
nextSpan.Slice(0, toCopy).CopyTo(destination.Slice(copied));
|
||||
copied += toCopy;
|
||||
if (copied >= destination.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
187
mcs/class/corlib/corefx/SequenceReaderExtensions.Binary.cs
Normal file
187
mcs/class/corlib/corefx/SequenceReaderExtensions.Binary.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
// 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.Binary;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Internal.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
#if !__MonoCS__
|
||||
public static partial class SequenceReaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Try to read the given type out of the buffer if possible. Warning: this is dangerous to use with arbitrary
|
||||
/// structs- see remarks for full details.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// IMPORTANT: The read is a straight copy of bits. If a struct depends on specific state of it's members to
|
||||
/// behave correctly this can lead to exceptions, etc. If reading endian specific integers, use the explicit
|
||||
/// overloads such as <see cref="TryReadLittleEndian(ref SequenceReader{byte}, out short)"/>
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// True if successful. <paramref name="value"/> will be default if failed (due to lack of space).
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static unsafe bool TryRead<T>(ref this SequenceReader<byte> reader, out T value) where T : unmanaged
|
||||
{
|
||||
ReadOnlySpan<byte> span = reader.UnreadSpan;
|
||||
if (span.Length < sizeof(T))
|
||||
return TryReadMultisegment(ref reader, out value);
|
||||
|
||||
value = Unsafe.ReadUnaligned<T>(ref MemoryMarshal.GetReference(span));
|
||||
reader.Advance(sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static unsafe bool TryReadMultisegment<T>(ref SequenceReader<byte> reader, out T value) where T : unmanaged
|
||||
{
|
||||
Debug.Assert(reader.UnreadSpan.Length < sizeof(T));
|
||||
|
||||
// Not enough data in the current segment, try to peek for the data we need.
|
||||
T buffer = default;
|
||||
Span<byte> tempSpan = new Span<byte>(&buffer, sizeof(T));
|
||||
|
||||
if (!reader.TryCopyTo(tempSpan))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = Unsafe.ReadUnaligned<T>(ref MemoryMarshal.GetReference(tempSpan));
|
||||
reader.Advance(sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an <see cref="short"/> as little endian.
|
||||
/// </summary>
|
||||
/// <returns>False if there wasn't enough data for an <see cref="short"/>.</returns>
|
||||
public static bool TryReadLittleEndian(ref this SequenceReader<byte> reader, out short value)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
return reader.TryRead(out value);
|
||||
}
|
||||
|
||||
return TryReadReverseEndianness(ref reader, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an <see cref="short"/> as big endian.
|
||||
/// </summary>
|
||||
/// <returns>False if there wasn't enough data for an <see cref="short"/>.</returns>
|
||||
public static bool TryReadBigEndian(ref this SequenceReader<byte> reader, out short value)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
return reader.TryRead(out value);
|
||||
}
|
||||
|
||||
return TryReadReverseEndianness(ref reader, out value);
|
||||
}
|
||||
|
||||
private static bool TryReadReverseEndianness(ref SequenceReader<byte> reader, out short value)
|
||||
{
|
||||
if (reader.TryRead(out value))
|
||||
{
|
||||
value = BinaryPrimitives.ReverseEndianness(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an <see cref="int"/> as little endian.
|
||||
/// </summary>
|
||||
/// <returns>False if there wasn't enough data for an <see cref="int"/>.</returns>
|
||||
public static bool TryReadLittleEndian(ref this SequenceReader<byte> reader, out int value)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
return reader.TryRead(out value);
|
||||
}
|
||||
|
||||
return TryReadReverseEndianness(ref reader, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an <see cref="int"/> as big endian.
|
||||
/// </summary>
|
||||
/// <returns>False if there wasn't enough data for an <see cref="int"/>.</returns>
|
||||
public static bool TryReadBigEndian(ref this SequenceReader<byte> reader, out int value)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
return reader.TryRead(out value);
|
||||
}
|
||||
|
||||
return TryReadReverseEndianness(ref reader, out value);
|
||||
}
|
||||
|
||||
private static bool TryReadReverseEndianness(ref SequenceReader<byte> reader, out int value)
|
||||
{
|
||||
if (reader.TryRead(out value))
|
||||
{
|
||||
value = BinaryPrimitives.ReverseEndianness(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an <see cref="long"/> as little endian.
|
||||
/// </summary>
|
||||
/// <returns>False if there wasn't enough data for an <see cref="long"/>.</returns>
|
||||
public static bool TryReadLittleEndian(ref this SequenceReader<byte> reader, out long value)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
return reader.TryRead(out value);
|
||||
}
|
||||
|
||||
return TryReadReverseEndianness(ref reader, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an <see cref="long"/> as big endian.
|
||||
/// </summary>
|
||||
/// <returns>False if there wasn't enough data for an <see cref="long"/>.</returns>
|
||||
public static bool TryReadBigEndian(ref this SequenceReader<byte> reader, out long value)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
return reader.TryRead(out value);
|
||||
}
|
||||
|
||||
return TryReadReverseEndianness(ref reader, out value);
|
||||
}
|
||||
|
||||
private static bool TryReadReverseEndianness(ref SequenceReader<byte> reader, out long value)
|
||||
{
|
||||
if (reader.TryRead(out value))
|
||||
{
|
||||
value = BinaryPrimitives.ReverseEndianness(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
public static partial class SequenceReaderExtensions
|
||||
{
|
||||
public static bool TryReadBigEndian(this System.Buffers.SequenceReader<byte> reader, out short value) => throw new PlatformNotSupportedException();
|
||||
public static bool TryReadBigEndian(this System.Buffers.SequenceReader<byte> reader, out int value) => throw new PlatformNotSupportedException();
|
||||
public static bool TryReadBigEndian(this System.Buffers.SequenceReader<byte> reader, out long value) => throw new PlatformNotSupportedException();
|
||||
public static bool TryReadLittleEndian(this System.Buffers.SequenceReader<byte> reader, out short value) => throw new PlatformNotSupportedException();
|
||||
public static bool TryReadLittleEndian(this System.Buffers.SequenceReader<byte> reader, out int value) => throw new PlatformNotSupportedException();
|
||||
public static bool TryReadLittleEndian(this System.Buffers.SequenceReader<byte> reader, out long value) => throw new PlatformNotSupportedException();
|
||||
}
|
||||
#endif
|
||||
}
|
Reference in New Issue
Block a user