// 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
{
///
/// 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.
///
///
/// 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
///
///
/// True if successful. will be default if failed (due to lack of space).
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe bool TryRead(ref this SequenceReader reader, out T value) where T : unmanaged
{
ReadOnlySpan span = reader.UnreadSpan;
if (span.Length < sizeof(T))
return TryReadMultisegment(ref reader, out value);
value = Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(span));
reader.Advance(sizeof(T));
return true;
}
private static unsafe bool TryReadMultisegment(ref SequenceReader 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 tempSpan = new Span(&buffer, sizeof(T));
if (!reader.TryCopyTo(tempSpan))
{
value = default;
return false;
}
value = Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(tempSpan));
reader.Advance(sizeof(T));
return true;
}
///
/// Reads an as little endian.
///
/// False if there wasn't enough data for an .
public static bool TryReadLittleEndian(ref this SequenceReader reader, out short value)
{
if (BitConverter.IsLittleEndian)
{
return reader.TryRead(out value);
}
return TryReadReverseEndianness(ref reader, out value);
}
///
/// Reads an as big endian.
///
/// False if there wasn't enough data for an .
public static bool TryReadBigEndian(ref this SequenceReader reader, out short value)
{
if (!BitConverter.IsLittleEndian)
{
return reader.TryRead(out value);
}
return TryReadReverseEndianness(ref reader, out value);
}
private static bool TryReadReverseEndianness(ref SequenceReader reader, out short value)
{
if (reader.TryRead(out value))
{
value = BinaryPrimitives.ReverseEndianness(value);
return true;
}
return false;
}
///
/// Reads an as little endian.
///
/// False if there wasn't enough data for an .
public static bool TryReadLittleEndian(ref this SequenceReader reader, out int value)
{
if (BitConverter.IsLittleEndian)
{
return reader.TryRead(out value);
}
return TryReadReverseEndianness(ref reader, out value);
}
///
/// Reads an as big endian.
///
/// False if there wasn't enough data for an .
public static bool TryReadBigEndian(ref this SequenceReader reader, out int value)
{
if (!BitConverter.IsLittleEndian)
{
return reader.TryRead(out value);
}
return TryReadReverseEndianness(ref reader, out value);
}
private static bool TryReadReverseEndianness(ref SequenceReader reader, out int value)
{
if (reader.TryRead(out value))
{
value = BinaryPrimitives.ReverseEndianness(value);
return true;
}
return false;
}
///
/// Reads an as little endian.
///
/// False if there wasn't enough data for an .
public static bool TryReadLittleEndian(ref this SequenceReader reader, out long value)
{
if (BitConverter.IsLittleEndian)
{
return reader.TryRead(out value);
}
return TryReadReverseEndianness(ref reader, out value);
}
///
/// Reads an as big endian.
///
/// False if there wasn't enough data for an .
public static bool TryReadBigEndian(ref this SequenceReader reader, out long value)
{
if (!BitConverter.IsLittleEndian)
{
return reader.TryRead(out value);
}
return TryReadReverseEndianness(ref reader, out value);
}
private static bool TryReadReverseEndianness(ref SequenceReader 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 reader, out short value) => throw new PlatformNotSupportedException();
public static bool TryReadBigEndian(this System.Buffers.SequenceReader reader, out int value) => throw new PlatformNotSupportedException();
public static bool TryReadBigEndian(this System.Buffers.SequenceReader reader, out long value) => throw new PlatformNotSupportedException();
public static bool TryReadLittleEndian(this System.Buffers.SequenceReader reader, out short value) => throw new PlatformNotSupportedException();
public static bool TryReadLittleEndian(this System.Buffers.SequenceReader reader, out int value) => throw new PlatformNotSupportedException();
public static bool TryReadLittleEndian(this System.Buffers.SequenceReader reader, out long value) => throw new PlatformNotSupportedException();
}
#endif
}