Imported Upstream version 6.6.0.103

Former-commit-id: ed8637df0061cb6f128ae1d26d26db7734e0fa0d
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2019-10-01 09:12:40 +00:00
parent 53ae335b9c
commit 9b1ef54dc9
78 changed files with 2577 additions and 322 deletions

View 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));
}
}
}

View 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; }
}
}

View File

@@ -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}.";
}

File diff suppressed because it is too large Load Diff

View 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;
}
}
}

View 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
}