// Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. /* 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; using System.Buffers.Binary; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace MessagePack { internal 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 its 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; } #if UNITY_ANDROID /// /// In Android 32bit device(armv7) + IL2CPP does not work correctly on Unsafe.ReadUnaligned. /// Perhaps it is about memory alignment bug of Unity's IL2CPP VM. /// For a workaround, read memory manually. /// https://github.com/neuecc/MessagePack-CSharp/issues/748 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static unsafe bool TryRead(ref this SequenceReader reader, out long value) { ReadOnlySpan span = reader.UnreadSpan; if (span.Length < sizeof(long)) { return TryReadMultisegment(ref reader, out value); } value = MessagePack.SafeBitConverter.ToInt64(span); reader.Advance(sizeof(long)); return true; } #endif private static unsafe bool TryReadMultisegment(ref SequenceReader reader, out T value) where T : unmanaged { Debug.Assert(reader.UnreadSpan.Length < sizeof(T), "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; } #if UNITY_ANDROID private static unsafe bool TryReadMultisegment(ref SequenceReader reader, out long value) { Debug.Assert(reader.UnreadSpan.Length < sizeof(long), "reader.UnreadSpan.Length < sizeof(long)"); // Not enough data in the current segment, try to peek for the data we need. long buffer = default; Span tempSpan = new Span(&buffer, sizeof(long)); if (!reader.TryCopyTo(tempSpan)) { value = default; return false; } value = MessagePack.SafeBitConverter.ToInt64(tempSpan); reader.Advance(sizeof(long)); return true; } #endif /// /// Reads an from the next position in the sequence. /// /// The reader to read from. /// Receives the value read. /// true if there was another byte in the sequence; false otherwise. public static bool TryRead(ref this SequenceReader reader, out sbyte value) { if (TryRead(ref reader, out byte byteValue)) { value = unchecked((sbyte)byteValue); return true; } value = default; return false; } /// /// 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); } /// /// Reads an as big endian. /// /// False if there wasn't enough data for an . public static bool TryReadBigEndian(ref this SequenceReader reader, out ushort value) { if (TryReadBigEndian(ref reader, out short shortValue)) { value = unchecked((ushort)shortValue); return true; } value = default; return false; } 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 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); } /// /// Reads an as big endian. /// /// False if there wasn't enough data for an . public static bool TryReadBigEndian(ref this SequenceReader reader, out uint value) { if (TryReadBigEndian(ref reader, out int intValue)) { value = unchecked((uint)intValue); return true; } value = default; return false; } 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 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); } /// /// Reads an as big endian. /// /// False if there wasn't enough data for an . public static bool TryReadBigEndian(ref this SequenceReader reader, out ulong value) { if (TryReadBigEndian(ref reader, out long longValue)) { value = unchecked((ulong)longValue); return true; } value = default; return false; } private static bool TryReadReverseEndianness(ref SequenceReader reader, out long value) { if (reader.TryRead(out value)) { value = BinaryPrimitives.ReverseEndianness(value); return true; } return false; } /// /// Reads a as big endian. /// /// False if there wasn't enough data for a . public static unsafe bool TryReadBigEndian(ref this SequenceReader reader, out float value) { if (TryReadBigEndian(ref reader, out int intValue)) { value = *(float*)&intValue; return true; } value = default; return false; } /// /// Reads a as big endian. /// /// False if there wasn't enough data for a . public static unsafe bool TryReadBigEndian(ref this SequenceReader reader, out double value) { if (TryReadBigEndian(ref reader, out long longValue)) { value = *(double*)&longValue; return true; } value = default; return false; } } }