From: mikedn Date: Mon, 27 Feb 2017 20:44:27 +0000 (+0200) Subject: Improve BitConverter.ToIntNN methods (dotnet/coreclr#9803) X-Git-Tag: submit/tizen/20210909.063632~11030^2~7933 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=05feb661328682976b8befde0625939d524efc82;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Improve BitConverter.ToIntNN methods (dotnet/coreclr#9803) * Improve BitConverter.ToIntNN methods - Avoid duplicate argument validation - Fix failed optimization attempt: (uint)i < a.Length needs to be (uint) < (uint)a.Length otherwise it gets promoted to long and that's bad for 32 bit architectures - Workaround JIT limitation: fixed variables cannot be enregistered - Use Unsafe.ReadUnaligned to avoid alignment handling complications - Use Unsafe.As instead of fixed as it generates shorter code - Don't forward unsigned method to signed methods. The JIT doesn't want to inline the signed methods even though the generated code isn't significantly larger Commit migrated from https://github.com/dotnet/coreclr/commit/ac1dcaef3e9ddb5290b555db2aefca2adfe52419 --- diff --git a/src/coreclr/src/mscorlib/src/System/BitConverter.cs b/src/coreclr/src/mscorlib/src/System/BitConverter.cs index caa0ae5..a17a067 100644 --- a/src/coreclr/src/mscorlib/src/System/BitConverter.cs +++ b/src/coreclr/src/mscorlib/src/System/BitConverter.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; using System.Security; namespace System @@ -38,47 +39,46 @@ namespace System public static byte[] GetBytes(char value) { Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 2); + Contract.Ensures(Contract.Result().Length == sizeof(char)); - return GetBytes((short)value); + byte[] bytes = new byte[sizeof(char)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; } // Converts a short into an array of bytes with length // two. - public static unsafe byte[] GetBytes(short value) + public static byte[] GetBytes(short value) { Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 2); + Contract.Ensures(Contract.Result().Length == sizeof(short)); - byte[] bytes = new byte[2]; - fixed (byte* b = &bytes[0]) - *((short*)b) = value; + byte[] bytes = new byte[sizeof(short)]; + Unsafe.As(ref bytes[0]) = value; return bytes; } // Converts an int into an array of bytes with length // four. - public static unsafe byte[] GetBytes(int value) + public static byte[] GetBytes(int value) { Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 4); + Contract.Ensures(Contract.Result().Length == sizeof(int)); - byte[] bytes = new byte[4]; - fixed (byte* b = &bytes[0]) - *((int*)b) = value; + byte[] bytes = new byte[sizeof(int)]; + Unsafe.As(ref bytes[0]) = value; return bytes; } // Converts a long into an array of bytes with length // eight. - public static unsafe byte[] GetBytes(long value) + public static byte[] GetBytes(long value) { Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 8); + Contract.Ensures(Contract.Result().Length == sizeof(long)); - byte[] bytes = new byte[8]; - fixed (byte* b = &bytes[0]) - *((long*)b) = value; + byte[] bytes = new byte[sizeof(long)]; + Unsafe.As(ref bytes[0]) = value; return bytes; } @@ -88,9 +88,11 @@ namespace System public static byte[] GetBytes(ushort value) { Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 2); + Contract.Ensures(Contract.Result().Length == sizeof(ushort)); - return GetBytes(unchecked((short)value)); + byte[] bytes = new byte[sizeof(ushort)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; } // Converts an uint into an array of bytes with @@ -99,9 +101,11 @@ namespace System public static byte[] GetBytes(uint value) { Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 4); + Contract.Ensures(Contract.Result().Length == sizeof(uint)); - return GetBytes(unchecked((int)value)); + byte[] bytes = new byte[sizeof(uint)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; } // Converts an unsigned long into an array of bytes with @@ -110,212 +114,114 @@ namespace System public static byte[] GetBytes(ulong value) { Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 8); + Contract.Ensures(Contract.Result().Length == sizeof(ulong)); - return GetBytes(unchecked((long)value)); + byte[] bytes = new byte[sizeof(ulong)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; } // Converts a float into an array of bytes with length // four. - public static unsafe byte[] GetBytes(float value) + public static byte[] GetBytes(float value) { Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 4); + Contract.Ensures(Contract.Result().Length == sizeof(float)); - return GetBytes(*(int*)&value); + byte[] bytes = new byte[sizeof(float)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; } // Converts a double into an array of bytes with length // eight. - public static unsafe byte[] GetBytes(double value) + public static byte[] GetBytes(double value) { Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 8); + Contract.Ensures(Contract.Result().Length == sizeof(double)); - return GetBytes(*(long*)&value); + byte[] bytes = new byte[sizeof(double)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; } // Converts an array of bytes into a char. - public static char ToChar(byte[] value, int startIndex) + public static char ToChar(byte[] value, int startIndex) => unchecked((char)ReadInt16(value, startIndex)); + + private static short ReadInt16(byte[] value, int startIndex) { if (value == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); - if (unchecked((uint)startIndex) >= value.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);; - if (startIndex > value.Length - 2) + if (unchecked((uint)startIndex) >= unchecked((uint)value.Length)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (startIndex > value.Length - sizeof(short)) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); Contract.EndContractBlock(); - return unchecked((char)ToInt16(value, startIndex)); + return Unsafe.ReadUnaligned(ref value[startIndex]); } - // Converts an array of bytes into a short. - public static unsafe short ToInt16(byte[] value, int startIndex) + private static int ReadInt32(byte[] value, int startIndex) { if (value == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); - if (unchecked((uint)startIndex) >= value.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);; - if (startIndex > value.Length - 2) + if (unchecked((uint)startIndex) >= unchecked((uint)value.Length)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (startIndex > value.Length - sizeof(int)) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); Contract.EndContractBlock(); - fixed (byte* pbyte = &value[startIndex]) - { - if (startIndex % 2 == 0) - { - // data is aligned - return *((short*)pbyte); - } - else if (IsLittleEndian) - { - return (short)((*pbyte) | (*(pbyte + 1) << 8)); - } - else - { - return (short)((*pbyte << 8) | (*(pbyte + 1))); - } - } + return Unsafe.ReadUnaligned(ref value[startIndex]); } - // Converts an array of bytes into an int. - public static unsafe int ToInt32(byte[] value, int startIndex) + private static long ReadInt64(byte[] value, int startIndex) { if (value == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); - if (unchecked((uint)startIndex) >= value.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);; - if (startIndex > value.Length - 4) + if (unchecked((uint)startIndex) >= unchecked((uint)value.Length)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (startIndex > value.Length - sizeof(long)) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); Contract.EndContractBlock(); - fixed (byte* pbyte = &value[startIndex]) - { - if (startIndex % 4 == 0) - { - // data is aligned - return *((int*)pbyte); - } - else if (IsLittleEndian) - { - return (*pbyte) | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24); - } - else - { - return (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3)); - } - } + return Unsafe.ReadUnaligned(ref value[startIndex]); } - // Converts an array of bytes into a long. - public static unsafe long ToInt64(byte[] value, int startIndex) - { - if (value == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); - if (unchecked((uint)startIndex) >= value.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);; - if (startIndex > value.Length - 8) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); - Contract.EndContractBlock(); + // Converts an array of bytes into a short. + public static short ToInt16(byte[] value, int startIndex) => ReadInt16(value, startIndex); - fixed (byte* pbyte = &value[startIndex]) - { - if (startIndex % 8 == 0) - { - // data is aligned - return *((long*)pbyte); - } - else if (IsLittleEndian) - { - int i1 = (*pbyte) | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24); - int i2 = (*(pbyte + 4)) | (*(pbyte + 5) << 8) | (*(pbyte + 6) << 16) | (*(pbyte + 7) << 24); - return unchecked((uint)i1) | ((long)i2 << 32); - } - else - { - int i1 = (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3)); - int i2 = (*(pbyte + 4) << 24) | (*(pbyte + 5) << 16) | (*(pbyte + 6) << 8) | (*(pbyte + 7)); - return unchecked((uint)i2) | ((long)i1 << 32); - } - } - } + // Converts an array of bytes into an int. + public static int ToInt32(byte[] value, int startIndex) => ReadInt32(value, startIndex); + // Converts an array of bytes into a long. + public static long ToInt64(byte[] value, int startIndex) => ReadInt64(value, startIndex); // Converts an array of bytes into an ushort. // [CLSCompliant(false)] - public static ushort ToUInt16(byte[] value, int startIndex) - { - if (value == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); - if (unchecked((uint)startIndex) >= value.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);; - if (startIndex > value.Length - 2) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); - Contract.EndContractBlock(); - - return unchecked((ushort)ToInt16(value, startIndex)); - } + public static ushort ToUInt16(byte[] value, int startIndex) => unchecked((ushort)ReadInt16(value, startIndex)); // Converts an array of bytes into an uint. // [CLSCompliant(false)] - public static uint ToUInt32(byte[] value, int startIndex) - { - if (value == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); - if (unchecked((uint)startIndex) >= value.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);; - if (startIndex > value.Length - 4) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); - Contract.EndContractBlock(); - - return unchecked((uint)ToInt32(value, startIndex)); - } + public static uint ToUInt32(byte[] value, int startIndex) => unchecked((uint)ReadInt32(value, startIndex)); // Converts an array of bytes into an unsigned long. // [CLSCompliant(false)] - public static ulong ToUInt64(byte[] value, int startIndex) - { - if (value == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); - if (unchecked((uint)startIndex) >= value.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);; - if (startIndex > value.Length - 8) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); - Contract.EndContractBlock(); - - return unchecked((ulong)ToInt64(value, startIndex)); - } + public static ulong ToUInt64(byte[] value, int startIndex) => unchecked((ulong)ReadInt64(value, startIndex)); // Converts an array of bytes into a float. public static unsafe float ToSingle(byte[] value, int startIndex) { - if (value == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); - if (unchecked((uint)startIndex) >= value.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);; - if (startIndex > value.Length - 4) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); - Contract.EndContractBlock(); - - int val = ToInt32(value, startIndex); + int val = ReadInt32(value, startIndex); return *(float*)&val; } // Converts an array of bytes into a double. public static unsafe double ToDouble(byte[] value, int startIndex) { - if (value == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); - if (unchecked((uint)startIndex) >= value.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);; - if (startIndex > value.Length - 8) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); - Contract.EndContractBlock(); - - long val = ToInt64(value, startIndex); + long val = ReadInt64(value, startIndex); return *(double*)&val; }