Avoid using MemoryMarshal.Cast<byte, _> on Android

This centralizes *all* uses of this API to a `SafeBitConverter` class which does the efficient thing except on Android, where we fall back to a safe pattern.

Fixes #931
See also #748, which was a similar fix but only to two call sites.
This commit is contained in:
Andrew Arnott 2020-05-17 07:38:49 -06:00
Родитель 0f8645619e
Коммит 4d76ac860c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: A9B9910CDCCDA441
4 изменённых файлов: 79 добавлений и 27 удалений

Просмотреть файл

@ -102,6 +102,9 @@
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\SequenceReaderExtensions.cs">
<Link>Code\SequenceReaderExtensions.cs</Link>
</Compile>
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\SafeBitConverter.cs">
<Link>Code\SafeBitConverter.cs</Link>
</Compile>
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\MessagePackCode.cs">
<Link>Code\MessagePackCode.cs</Link>
</Compile>

Просмотреть файл

@ -427,7 +427,7 @@ namespace MessagePack.Internal
{
if (span.Length >= 8)
{
key = MemoryMarshal.Cast<byte, ulong>(span)[0];
key = SafeBitConverter.ToUInt64(span);
span = span.Slice(8);
}
else
@ -443,7 +443,7 @@ namespace MessagePack.Internal
case 2:
{
key = MemoryMarshal.Cast<byte, ushort>(span)[0];
key = SafeBitConverter.ToUInt16(span);
span = span.Slice(2);
break;
}
@ -451,7 +451,7 @@ namespace MessagePack.Internal
case 3:
{
var a = span[0];
var b = MemoryMarshal.Cast<byte, ushort>(span.Slice(1))[0];
var b = SafeBitConverter.ToUInt16(span.Slice(1));
key = a | (ulong)b << 8;
span = span.Slice(3);
break;
@ -459,7 +459,7 @@ namespace MessagePack.Internal
case 4:
{
key = MemoryMarshal.Cast<byte, uint>(span)[0];
key = SafeBitConverter.ToUInt32(span);
span = span.Slice(4);
break;
}
@ -467,7 +467,7 @@ namespace MessagePack.Internal
case 5:
{
var a = span[0];
var b = MemoryMarshal.Cast<byte, uint>(span.Slice(1))[0];
var b = SafeBitConverter.ToUInt32(span.Slice(1));
key = a | (ulong)b << 8;
span = span.Slice(5);
break;
@ -475,8 +475,8 @@ namespace MessagePack.Internal
case 6:
{
ulong a = MemoryMarshal.Cast<byte, ushort>(span)[0];
ulong b = MemoryMarshal.Cast<byte, uint>(span.Slice(2))[0];
ulong a = SafeBitConverter.ToUInt16(span);
ulong b = SafeBitConverter.ToUInt32(span.Slice(2));
key = a | (b << 16);
span = span.Slice(6);
break;
@ -485,8 +485,8 @@ namespace MessagePack.Internal
case 7:
{
var a = span[0];
var b = MemoryMarshal.Cast<byte, ushort>(span.Slice(1))[0];
var c = MemoryMarshal.Cast<byte, uint>(span.Slice(3))[0];
var b = SafeBitConverter.ToUInt16(span.Slice(1));
var c = SafeBitConverter.ToUInt32(span.Slice(3));
key = a | (ulong)b << 8 | (ulong)c << 24;
span = span.Slice(7);
break;

Просмотреть файл

@ -0,0 +1,65 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Runtime.InteropServices;
namespace MessagePack
{
internal static class SafeBitConverter
{
internal static long ToInt64(ReadOnlySpan<byte> value)
{
#if UNITY_ANDROID
if (BitConverter.IsLittleEndian)
{
int i1 = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
int i2 = value[4] | (value[5] << 8) | (value[6] << 16) | (value[7] << 24);
return (uint)i1 | ((long)i2 << 32);
}
else
{
int i1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3];
int i2 = (value[4] << 24) | (value[5] << 16) | (value[6] << 8) | value[7];
return (uint)i2 | ((long)i1 << 32);
}
#else
return MemoryMarshal.Cast<byte, long>(value)[0];
#endif
}
internal static ulong ToUInt64(ReadOnlySpan<byte> value) => unchecked((ulong)ToInt64(value));
internal static ushort ToUInt16(ReadOnlySpan<byte> value)
{
#if UNITY_ANDROID
if (BitConverter.IsLittleEndian)
{
return (ushort)(value[0] | (value[1] << 8));
}
else
{
return (ushort)((value[0] << 8) | value[1]);
}
#else
return MemoryMarshal.Cast<byte, ushort>(value)[0];
#endif
}
internal static uint ToUInt32(ReadOnlySpan<byte> value)
{
#if UNITY_ANDROID
if (BitConverter.IsLittleEndian)
{
return (uint)(value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24));
}
else
{
return (uint)((value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]);
}
#else
return MemoryMarshal.Cast<byte, uint>(value)[0];
#endif
}
}
}

Просмотреть файл

@ -58,27 +58,11 @@ namespace System.Buffers
return TryReadMultisegment(ref reader, out value);
}
value = BitConverterToInt64(span);
value = SafeBitConverter.ToInt64(span);
reader.Advance(sizeof(long));
return true;
}
private static unsafe long BitConverterToInt64(ReadOnlySpan<byte> value)
{
if (BitConverter.IsLittleEndian)
{
int i1 = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
int i2 = value[4] | (value[5] << 8) | (value[6] << 16) | (value[7] << 24);
return (uint)i1 | ((long)i2 << 32);
}
else
{
int i1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3];
int i2 = (value[4] << 24) | (value[5] << 16) | (value[6] << 8) | value[7];
return (uint)i2 | ((long)i1 << 32);
}
}
#endif
private static unsafe bool TryReadMultisegment<T>(ref SequenceReader<byte> reader, out T value)
@ -117,7 +101,7 @@ namespace System.Buffers
return false;
}
value = BitConverterToInt64(tempSpan);
value = SafeBitConverter.ToInt64(tempSpan);
reader.Advance(sizeof(long));
return true;
}