// This file is provided under The MIT License as part of RiptideNetworking. // Copyright (c) Tom Weiland // For additional information please see the included LICENSE.md file or view it on GitHub: // https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Riptide.Utils { /// Provides functionality for converting bits and bytes to various value types and vice versa. public class Converter { /// The number of bits in a byte. public const int BitsPerByte = 8; /// The number of bits in a ulong. public const int BitsPerULong = sizeof(ulong) * BitsPerByte; #region Zig Zag Encoding /// Zig zag encodes . /// The value to encode. /// The zig zag-encoded value. /// Zig zag encoding allows small negative numbers to be represented as small positive numbers. All positive numbers are doubled and become even numbers, /// while all negative numbers become positive odd numbers. In contrast, simply casting a negative value to its unsigned counterpart would result in a large positive /// number which uses the high bit, rendering compression via and ineffective. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ZigZagEncode(int value) { return (value >> 31) ^ (value << 1); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long ZigZagEncode(long value) { return (value >> 63) ^ (value << 1); } /// Zig zag decodes . /// The value to decode. /// The zig zag-decoded value. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ZigZagDecode(int value) { return (value >> 1) ^ -(value & 1); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long ZigZagDecode(long value) { return (value >> 1) ^ -(value & 1); } #endregion #region Bits /// Takes bits from and writes them into , starting at . /// The bitfield from which to write the bits into the array. /// The number of bits to write. /// The array to write the bits into. /// The bit position in the array at which to start writing. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetBits(byte bitfield, int amount, byte[] array, int startBit) { byte mask = (byte)((1 << amount) - 1); bitfield &= mask; // Discard any bits that are set beyond the ones we're setting int inverseMask = ~mask; int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; if (bit == 0) array[pos] = (byte)(bitfield | (array[pos] & inverseMask)); else { array[pos ] = (byte)((bitfield << bit) | (array[pos] & ~(mask << bit))); array[pos + 1] = (byte)((bitfield >> (8 - bit)) | (array[pos + 1] & (inverseMask >> (8 - bit)))); } } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetBits(ushort bitfield, int amount, byte[] array, int startBit) { ushort mask = (ushort)((1 << amount) - 1); bitfield &= mask; // Discard any bits that are set beyond the ones we're setting int inverseMask = ~mask; int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; if (bit == 0) { array[pos ] = (byte)(bitfield | (array[pos] & inverseMask)); array[pos + 1] = (byte)((bitfield >> 8) | (array[pos + 1] & (inverseMask >> 8))); } else { array[pos ] = (byte)((bitfield << bit) | (array[pos] & ~(mask << bit))); bitfield >>= 8 - bit; inverseMask >>= 8 - bit; array[pos + 1] = (byte)(bitfield | (array[pos + 1] & inverseMask)); array[pos + 2] = (byte)((bitfield >> 8) | (array[pos + 2] & (inverseMask >> 8))); } } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetBits(uint bitfield, int amount, byte[] array, int startBit) { uint mask = (1u << (amount - 1) << 1) - 1; // Perform 2 shifts, doing it in 1 doesn't cause the value to wrap properly bitfield &= mask; // Discard any bits that are set beyond the ones we're setting uint inverseMask = ~mask; int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; if (bit == 0) { array[pos ] = (byte)(bitfield | (array[pos] & inverseMask)); array[pos + 1] = (byte)((bitfield >> 8) | (array[pos + 1] & (inverseMask >> 8))); array[pos + 2] = (byte)((bitfield >> 16) | (array[pos + 2] & (inverseMask >> 16))); array[pos + 3] = (byte)((bitfield >> 24) | (array[pos + 3] & (inverseMask >> 24))); } else { array[pos ] = (byte)((bitfield << bit) | (array[pos] & ~(mask << bit))); bitfield >>= 8 - bit; inverseMask >>= 8 - bit; array[pos + 1] = (byte)(bitfield | (array[pos + 1] & inverseMask)); array[pos + 2] = (byte)((bitfield >> 8) | (array[pos + 2] & (inverseMask >> 8))); array[pos + 3] = (byte)((bitfield >> 16) | (array[pos + 3] & (inverseMask >> 16))); array[pos + 4] = (byte)((bitfield >> 24) | (array[pos + 4] & ~(mask >> (32 - bit)))); // This one can't use inverseMask because it would have incorrectly zeroed bits } } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetBits(ulong bitfield, int amount, byte[] array, int startBit) { ulong mask = (1ul << (amount - 1) << 1) - 1; // Perform 2 shifts, doing it in 1 doesn't cause the value to wrap properly bitfield &= mask; // Discard any bits that are set beyond the ones we're setting ulong inverseMask = ~mask; int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; if (bit == 0) { array[pos ] = (byte)(bitfield | (array[pos] & inverseMask)); array[pos + 1] = (byte)((bitfield >> 8) | (array[pos + 1] & (inverseMask >> 8))); array[pos + 2] = (byte)((bitfield >> 16) | (array[pos + 2] & (inverseMask >> 16))); array[pos + 3] = (byte)((bitfield >> 24) | (array[pos + 3] & (inverseMask >> 24))); array[pos + 4] = (byte)((bitfield >> 32) | (array[pos + 4] & (inverseMask >> 32))); array[pos + 5] = (byte)((bitfield >> 40) | (array[pos + 5] & (inverseMask >> 40))); array[pos + 6] = (byte)((bitfield >> 48) | (array[pos + 6] & (inverseMask >> 48))); array[pos + 7] = (byte)((bitfield >> 56) | (array[pos + 7] & (inverseMask >> 56))); } else { array[pos ] = (byte)((bitfield << bit) | (array[pos] & ~(mask << bit))); bitfield >>= 8 - bit; inverseMask >>= 8 - bit; array[pos + 1] = (byte)(bitfield | (array[pos + 1] & inverseMask)); array[pos + 2] = (byte)((bitfield >> 8) | (array[pos + 2] & (inverseMask >> 8))); array[pos + 3] = (byte)((bitfield >> 16) | (array[pos + 3] & (inverseMask >> 16))); array[pos + 4] = (byte)((bitfield >> 24) | (array[pos + 4] & (inverseMask >> 24))); array[pos + 5] = (byte)((bitfield >> 32) | (array[pos + 5] & (inverseMask >> 32))); array[pos + 6] = (byte)((bitfield >> 40) | (array[pos + 6] & (inverseMask >> 40))); array[pos + 7] = (byte)((bitfield >> 48) | (array[pos + 7] & (inverseMask >> 48))); array[pos + 8] = (byte)((bitfield >> 56) | (array[pos + 8] & ~(mask >> (64 - bit)))); // This one can't use inverseMask because it would have incorrectly zeroed bits } } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetBits(ulong bitfield, int amount, ulong[] array, int startBit) { ulong mask = (1ul << (amount - 1) << 1) - 1; // Perform 2 shifts, doing it in 1 doesn't cause the value to wrap properly bitfield &= mask; // Discard any bits that are set beyond the ones we're setting int pos = startBit / BitsPerULong; int bit = startBit % BitsPerULong; if (bit == 0) array[pos] = bitfield | array[pos] & ~mask; else { array[pos] = (bitfield << bit) | (array[pos] & ~(mask << bit)); if (bit + amount >= BitsPerULong) array[pos + 1] = (bitfield >> (64 - bit)) | (array[pos + 1] & ~(mask >> (64 - bit))); } } /// Starting at , reads bits from into . /// The number of bits to read. /// The array to read the bits from. /// The bit position in the array at which to start reading. /// The bitfield into which to write the bits from the array. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void GetBits(int amount, byte[] array, int startBit, out byte bitfield) { bitfield = ByteFromBits(array, startBit); bitfield &= (byte)((1 << amount) - 1); // Discard any bits that are set beyond the ones we're reading } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void GetBits(int amount, byte[] array, int startBit, out ushort bitfield) { bitfield = UShortFromBits(array, startBit); bitfield &= (ushort)((1 << amount) - 1); // Discard any bits that are set beyond the ones we're reading } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void GetBits(int amount, byte[] array, int startBit, out uint bitfield) { bitfield = UIntFromBits(array, startBit); bitfield &= (1u << (amount - 1) << 1) - 1; // Discard any bits that are set beyond the ones we're reading } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void GetBits(int amount, byte[] array, int startBit, out ulong bitfield) { bitfield = ULongFromBits(array, startBit); bitfield &= (1ul << (amount - 1) << 1) - 1; // Discard any bits that are set beyond the ones we're reading } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void GetBits(int amount, ulong[] array, int startBit, out byte bitfield) { bitfield = ByteFromBits(array, startBit); bitfield &= (byte)((1 << amount) - 1); // Discard any bits that are set beyond the ones we're reading } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void GetBits(int amount, ulong[] array, int startBit, out ushort bitfield) { bitfield = UShortFromBits(array, startBit); bitfield &= (ushort)((1 << amount) - 1); // Discard any bits that are set beyond the ones we're reading } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void GetBits(int amount, ulong[] array, int startBit, out uint bitfield) { bitfield = UIntFromBits(array, startBit); bitfield &= (1u << (amount - 1) << 1) - 1; // Discard any bits that are set beyond the ones we're reading } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void GetBits(int amount, ulong[] array, int startBit, out ulong bitfield) { bitfield = ULongFromBits(array, startBit); bitfield &= (1ul << (amount - 1) << 1) - 1; // Discard any bits that are set beyond the ones we're reading } #endregion #region Byte/SByte /// Converts to 8 bits and writes them into at . /// The to convert. /// The array to write the bits into. /// The position in the array at which to write the bits. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SByteToBits(sbyte value, byte[] array, int startBit) => ByteToBits((byte)value, array, startBit); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SByteToBits(sbyte value, ulong[] array, int startBit) => ByteToBits((byte)value, array, startBit); /// Converts to 8 bits and writes them into at . /// The to convert. /// The array to write the bits into. /// The position in the array at which to write the bits. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ByteToBits(byte value, byte[] array, int startBit) { int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; if (bit == 0) array[pos] = value; else { array[pos ] |= (byte)(value << bit); array[pos + 1] = (byte)(value >> (8 - bit)); } } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ByteToBits(byte value, ulong[] array, int startBit) => ToBits(value, BitsPerByte, array, startBit); /// Converts the 8 bits at in to an . /// The array to convert the bits from. /// The position in the array from which to read the bits. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static sbyte SByteFromBits(byte[] array, int startBit) => (sbyte)ByteFromBits(array, startBit); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static sbyte SByteFromBits(ulong[] array, int startBit) => (sbyte)ByteFromBits(array, startBit); /// Converts the 8 bits at in to a . /// The array to convert the bits from. /// The position in the array from which to read the bits. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte ByteFromBits(byte[] array, int startBit) { int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; byte value = array[pos]; if (bit == 0) return value; value >>= bit; return (byte)(value | (array[pos + 1] << (8 - bit))); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte ByteFromBits(ulong[] array, int startBit) => (byte)FromBits(BitsPerByte, array, startBit); #endregion #region Bool /// Converts to a bit and writes it into at . /// The to convert. /// The array to write the bit into. /// The position in the array at which to write the bit. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void BoolToBit(bool value, byte[] array, int startBit) { int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; if (bit == 0) array[pos] = 0; if (value) array[pos] |= (byte)(1 << bit); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void BoolToBit(bool value, ulong[] array, int startBit) { int pos = startBit / BitsPerULong; int bit = startBit % BitsPerULong; if (bit == 0) array[pos] = 0; if (value) array[pos] |= 1ul << bit; } /// Converts the bit at in to a . /// The array to convert the bit from. /// The position in the array from which to read the bit. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool BoolFromBit(byte[] array, int startBit) { int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; return (array[pos] & (1 << bit)) != 0; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool BoolFromBit(ulong[] array, int startBit) { int pos = startBit / BitsPerULong; int bit = startBit % BitsPerULong; return (array[pos] & (1ul << bit)) != 0; } #endregion #region Short/UShort /// Converts a given to bytes and writes them into the given array. /// The to convert. /// The array to write the bytes into. /// The position in the array at which to write the bytes. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FromShort(short value, byte[] array, int startIndex) => FromUShort((ushort)value, array, startIndex); /// Converts a given to bytes and writes them into the given array. /// The to convert. /// The array to write the bytes into. /// The position in the array at which to write the bytes. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FromUShort(ushort value, byte[] array, int startIndex) { #if BIG_ENDIAN array[startIndex + 1] = (byte)value; array[startIndex ] = (byte)(value >> 8); #else array[startIndex ] = (byte)value; array[startIndex + 1] = (byte)(value >> 8); #endif } /// Converts the 2 bytes in the array at to a . /// The array to read the bytes from. /// The position in the array at which to read the bytes. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short ToShort(byte[] array, int startIndex) => (short)ToUShort(array, startIndex); /// Converts the 2 bytes in the array at to a . /// The array to read the bytes from. /// The position in the array at which to read the bytes. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ToUShort(byte[] array, int startIndex) { #if BIG_ENDIAN return (ushort)(array[startIndex + 1] | (array[startIndex ] << 8)); #else return (ushort)(array[startIndex ] | (array[startIndex + 1] << 8)); #endif } /// Converts to 16 bits and writes them into at . /// The to convert. /// The array to write the bits into. /// The position in the array at which to write the bits. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ShortToBits(short value, byte[] array, int startBit) => UShortToBits((ushort)value, array, startBit); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ShortToBits(short value, ulong[] array, int startBit) => UShortToBits((ushort)value, array, startBit); /// Converts to 16 bits and writes them into at . /// The to convert. /// The array to write the bits into. /// The position in the array at which to write the bits. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UShortToBits(ushort value, byte[] array, int startBit) { int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; if (bit == 0) { array[pos] = (byte)value; array[pos + 1] = (byte)(value >> 8); } else { array[pos ] |= (byte)(value << bit); value >>= 8 - bit; array[pos + 1] = (byte)value; array[pos + 2] = (byte)(value >> 8); } } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UShortToBits(ushort value, ulong[] array, int startBit) => ToBits(value, sizeof(ushort) * BitsPerByte, array, startBit); /// Converts the 16 bits at in to a . /// The array to convert the bits from. /// The position in the array from which to read the bits. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short ShortFromBits(byte[] array, int startBit) => (short)UShortFromBits(array, startBit); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short ShortFromBits(ulong[] array, int startBit) => (short)UShortFromBits(array, startBit); /// Converts the 16 bits at in to a . /// The array to convert the bits from. /// The position in the array from which to read the bits. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort UShortFromBits(byte[] array, int startBit) { int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; ushort value = (ushort)(array[pos] | (array[pos + 1] << 8)); if (bit == 0) return value; value >>= bit; return (ushort)(value | (array[pos + 2] << (16 - bit))); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort UShortFromBits(ulong[] array, int startBit) => (ushort)FromBits(sizeof(ushort) * BitsPerByte, array, startBit); #endregion #region Int/UInt /// Converts a given to bytes and writes them into the given array. /// The to convert. /// The array to write the bytes into. /// The position in the array at which to write the bytes. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FromInt(int value, byte[] array, int startIndex) => FromUInt((uint)value, array, startIndex); /// Converts a given to bytes and writes them into the given array. /// The to convert. /// The array to write the bytes into. /// The position in the array at which to write the bytes. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FromUInt(uint value, byte[] array, int startIndex) { #if BIG_ENDIAN array[startIndex + 3] = (byte)value; array[startIndex + 2] = (byte)(value >> 8); array[startIndex + 1] = (byte)(value >> 16); array[startIndex ] = (byte)(value >> 24); #else array[startIndex ] = (byte)value; array[startIndex + 1] = (byte)(value >> 8); array[startIndex + 2] = (byte)(value >> 16); array[startIndex + 3] = (byte)(value >> 24); #endif } /// Converts the 4 bytes in the array at to a . /// The array to read the bytes from. /// The position in the array at which to read the bytes. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ToInt(byte[] array, int startIndex) => (int)ToUInt(array, startIndex); /// Converts the 4 bytes in the array at to a . /// The array to read the bytes from. /// The position in the array at which to read the bytes. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ToUInt(byte[] array, int startIndex) { #if BIG_ENDIAN return (uint)(array[startIndex + 3] | (array[startIndex + 2] << 8) | (array[startIndex + 1] << 16) | (array[startIndex ] << 24)); #else return (uint)(array[startIndex ] | (array[startIndex + 1] << 8) | (array[startIndex + 2] << 16) | (array[startIndex + 3] << 24)); #endif } /// Converts to 32 bits and writes them into at . /// The to convert. /// The array to write the bits into. /// The position in the array at which to write the bits. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IntToBits(int value, byte[] array, int startBit) => UIntToBits((uint)value, array, startBit); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IntToBits(int value, ulong[] array, int startBit) => UIntToBits((uint)value, array, startBit); /// Converts to 32 bits and writes them into at . /// The to convert. /// The array to write the bits into. /// The position in the array at which to write the bits. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UIntToBits(uint value, byte[] array, int startBit) { int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; if (bit == 0) { array[pos ] = (byte)value; array[pos + 1] = (byte)(value >> 8); array[pos + 2] = (byte)(value >> 16); array[pos + 3] = (byte)(value >> 24); } else { array[pos ] |= (byte)(value << bit); value >>= 8 - bit; array[pos + 1] = (byte)value; array[pos + 2] = (byte)(value >> 8); array[pos + 3] = (byte)(value >> 16); array[pos + 4] = (byte)(value >> 24); } } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UIntToBits(uint value, ulong[] array, int startBit) => ToBits(value, sizeof(uint) * BitsPerByte, array, startBit); /// Converts the 32 bits at in to an . /// The array to convert the bits from. /// The position in the array from which to read the bits. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IntFromBits(byte[] array, int startBit) => (int)UIntFromBits(array, startBit); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IntFromBits(ulong[] array, int startBit) => (int)UIntFromBits(array, startBit); /// Converts the 32 bits at in to a . /// The array to convert the bits from. /// The position in the array from which to read the bits. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint UIntFromBits(byte[] array, int startBit) { int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; uint value = (uint)(array[pos] | (array[pos + 1] << 8) | (array[pos + 2] << 16) | (array[pos + 3] << 24)); if (bit == 0) return value; value >>= bit; return value | (uint)(array[pos + 4] << (32 - bit)); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint UIntFromBits(ulong[] array, int startBit) => (uint)FromBits(sizeof(uint) * BitsPerByte, array, startBit); #endregion #region Long/ULong /// Converts a given to bytes and writes them into the given array. /// The to convert. /// The array to write the bytes into. /// The position in the array at which to write the bytes. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FromLong(long value, byte[] array, int startIndex) => FromULong((ulong)value, array, startIndex); /// Converts a given to bytes and writes them into the given array. /// The to convert. /// The array to write the bytes into. /// The position in the array at which to write the bytes. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FromULong(ulong value, byte[] array, int startIndex) { #if BIG_ENDIAN array[startIndex + 7] = (byte)value; array[startIndex + 6] = (byte)(value >> 8); array[startIndex + 5] = (byte)(value >> 16); array[startIndex + 4] = (byte)(value >> 24); array[startIndex + 3] = (byte)(value >> 32); array[startIndex + 2] = (byte)(value >> 40); array[startIndex + 1] = (byte)(value >> 48); array[startIndex ] = (byte)(value >> 56); #else array[startIndex ] = (byte)value; array[startIndex + 1] = (byte)(value >> 8); array[startIndex + 2] = (byte)(value >> 16); array[startIndex + 3] = (byte)(value >> 24); array[startIndex + 4] = (byte)(value >> 32); array[startIndex + 5] = (byte)(value >> 40); array[startIndex + 6] = (byte)(value >> 48); array[startIndex + 7] = (byte)(value >> 56); #endif } /// Converts the 8 bytes in the array at to a . /// The array to read the bytes from. /// The position in the array at which to read the bytes. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long ToLong(byte[] array, int startIndex) { #if BIG_ENDIAN Array.Reverse(array, startIndex, longLength); #endif return BitConverter.ToInt64(array, startIndex); } /// Converts the 8 bytes in the array at to a . /// The array to read the bytes from. /// The position in the array at which to read the bytes. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong ToULong(byte[] array, int startIndex) { #if BIG_ENDIAN Array.Reverse(array, startIndex, ulongLength); #endif return BitConverter.ToUInt64(array, startIndex); } /// Converts to 64 bits and writes them into at . /// The to convert. /// The array to write the bits into. /// The position in the array at which to write the bits. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void LongToBits(long value, byte[] array, int startBit) => ULongToBits((ulong)value, array, startBit); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void LongToBits(long value, ulong[] array, int startBit) => ULongToBits((ulong)value, array, startBit); /// Converts to 64 bits and writes them into at . /// The to convert. /// The array to write the bits into. /// The position in the array at which to write the bits. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ULongToBits(ulong value, byte[] array, int startBit) { int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; if (bit == 0) { array[pos ] = (byte)value; array[pos + 1] = (byte)(value >> 8); array[pos + 2] = (byte)(value >> 16); array[pos + 3] = (byte)(value >> 24); array[pos + 4] = (byte)(value >> 32); array[pos + 5] = (byte)(value >> 40); array[pos + 6] = (byte)(value >> 48); array[pos + 7] = (byte)(value >> 56); } else { array[pos ] |= (byte)(value << bit); value >>= 8 - bit; array[pos + 1] = (byte)value; array[pos + 2] = (byte)(value >> 8); array[pos + 3] = (byte)(value >> 16); array[pos + 4] = (byte)(value >> 24); array[pos + 5] = (byte)(value >> 32); array[pos + 6] = (byte)(value >> 40); array[pos + 7] = (byte)(value >> 48); array[pos + 8] = (byte)(value >> 56); } } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ULongToBits(ulong value, ulong[] array, int startBit) { int pos = startBit / BitsPerULong; int bit = startBit % BitsPerULong; if (bit == 0) array[pos] = value; else { array[pos ] |= value << bit; array[pos + 1] = value >> (BitsPerULong - bit); } } /// Converts the 64 bits at in to a . /// The array to convert the bits from. /// The position in the array from which to read the bits. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long LongFromBits(byte[] array, int startBit) => (long)ULongFromBits(array, startBit); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long LongFromBits(ulong[] array, int startBit) => (long)ULongFromBits(array, startBit); /// Converts the 64 bits at in to a . /// The array to convert the bits from. /// The position in the array from which to read the bits. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong ULongFromBits(byte[] array, int startBit) { int pos = startBit / BitsPerByte; int bit = startBit % BitsPerByte; ulong value = BitConverter.ToUInt64(array, pos); if (bit == 0) return value; value >>= bit; return value | ((ulong)array[pos + 8] << (64 - bit)); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong ULongFromBits(ulong[] array, int startBit) { int pos = startBit / BitsPerULong; int bit = startBit % BitsPerULong; ulong value = array[pos]; if (bit == 0) return value; value >>= bit; return value | (array[pos + 1] << (BitsPerULong - bit)); } /// Converts to bits and writes them into at . /// Meant for values which fit into a , not for s themselves. /// The value to convert. /// The size in bits of the value being converted. /// The array to write the bits into. /// The position in the array at which to write the bits. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ToBits(ulong value, int valueSize, ulong[] array, int startBit) { int pos = startBit / BitsPerULong; int bit = startBit % BitsPerULong; if (bit == 0) array[pos] = value; else if (bit + valueSize < BitsPerULong) array[pos] |= value << bit; else { array[pos] |= value << bit; array[pos + 1] = value >> (BitsPerULong - bit); } } /// Converts the bits at in to a . /// Meant for values which fit into a , not for s themselves. /// The size in bits of the value being converted. /// The array to convert the bits from. /// The position in the array from which to read the bits. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong FromBits(int valueSize, ulong[] array, int startBit) { int pos = startBit / BitsPerULong; int bit = startBit % BitsPerULong; ulong value = array[pos]; if (bit == 0) return value; value >>= bit; if (bit + valueSize < BitsPerULong) return value; return value | (array[pos + 1] << (BitsPerULong - bit)); } #endregion #region Float /// Converts a given to bytes and writes them into the given array. /// The to convert. /// The array to write the bytes into. /// The position in the array at which to write the bytes. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FromFloat(float value, byte[] array, int startIndex) { FloatConverter converter = new FloatConverter { FloatValue = value }; #if BIG_ENDIAN array[startIndex + 3] = converter.Byte0; array[startIndex + 2] = converter.Byte1; array[startIndex + 1] = converter.Byte2; array[startIndex ] = converter.Byte3; #else array[startIndex ] = converter.Byte0; array[startIndex + 1] = converter.Byte1; array[startIndex + 2] = converter.Byte2; array[startIndex + 3] = converter.Byte3; #endif } /// Converts the 4 bytes in the array at to a . /// The array to read the bytes from. /// The position in the array at which to read the bytes. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ToFloat(byte[] array, int startIndex) { #if BIG_ENDIAN return new FloatConverter { Byte3 = array[startIndex], Byte2 = array[startIndex + 1], Byte1 = array[startIndex + 2], Byte0 = array[startIndex + 3] }.FloatValue; #else return new FloatConverter { Byte0 = array[startIndex], Byte1 = array[startIndex + 1], Byte2 = array[startIndex + 2], Byte3 = array[startIndex + 3] }.FloatValue; #endif } /// Converts to 32 bits and writes them into at . /// The to convert. /// The array to write the bits into. /// The position in the array at which to write the bits. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FloatToBits(float value, byte[] array, int startBit) { UIntToBits(new FloatConverter { FloatValue = value }.UIntValue, array, startBit); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FloatToBits(float value, ulong[] array, int startBit) { UIntToBits(new FloatConverter { FloatValue = value }.UIntValue, array, startBit); } /// Converts the 32 bits at in to a . /// The array to convert the bits from. /// The position in the array from which to read the bits. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float FloatFromBits(byte[] array, int startBit) { return new FloatConverter { UIntValue = UIntFromBits(array, startBit) }.FloatValue; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float FloatFromBits(ulong[] array, int startBit) { return new FloatConverter { UIntValue = UIntFromBits(array, startBit) }.FloatValue; } #endregion #region Double /// Converts a given to bytes and writes them into the given array. /// The to convert. /// The array to write the bytes into. /// The position in the array at which to write the bytes. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FromDouble(double value, byte[] array, int startIndex) { DoubleConverter converter = new DoubleConverter { DoubleValue = value }; #if BIG_ENDIAN array[startIndex + 7] = converter.Byte0; array[startIndex + 6] = converter.Byte1; array[startIndex + 5] = converter.Byte2; array[startIndex + 4] = converter.Byte3; array[startIndex + 3] = converter.Byte4; array[startIndex + 2] = converter.Byte5; array[startIndex + 1] = converter.Byte6; array[startIndex ] = converter.Byte7; #else array[startIndex ] = converter.Byte0; array[startIndex + 1] = converter.Byte1; array[startIndex + 2] = converter.Byte2; array[startIndex + 3] = converter.Byte3; array[startIndex + 4] = converter.Byte4; array[startIndex + 5] = converter.Byte5; array[startIndex + 6] = converter.Byte6; array[startIndex + 7] = converter.Byte7; #endif } /// Converts the 8 bytes in the array at to a . /// The array to read the bytes from. /// The position in the array at which to read the bytes. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double ToDouble(byte[] array, int startIndex) { #if BIG_ENDIAN Array.Reverse(array, startIndex, doubleLength); #endif return BitConverter.ToDouble(array, startIndex); } /// Converts to 64 bits and writes them into at . /// The to convert. /// The array to write the bits into. /// The position in the array at which to write the bits. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void DoubleToBits(double value, byte[] array, int startBit) { ULongToBits(new DoubleConverter { DoubleValue = value }.ULongValue, array, startBit); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void DoubleToBits(double value, ulong[] array, int startBit) { ULongToBits(new DoubleConverter { DoubleValue = value }.ULongValue, array, startBit); } /// Converts the 64 bits at in to a . /// The array to convert the bits from. /// The position in the array from which to read the bits. /// The converted . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double DoubleFromBits(byte[] array, int startBit) { return new DoubleConverter { ULongValue = ULongFromBits(array, startBit) }.DoubleValue; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double DoubleFromBits(ulong[] array, int startBit) { return new DoubleConverter { ULongValue = ULongFromBits(array, startBit) }.DoubleValue; } #endregion } [StructLayout(LayoutKind.Explicit)] internal struct FloatConverter { [FieldOffset(0)] public byte Byte0; [FieldOffset(1)] public byte Byte1; [FieldOffset(2)] public byte Byte2; [FieldOffset(3)] public byte Byte3; [FieldOffset(0)] public float FloatValue; [FieldOffset(0)] public uint UIntValue; } [StructLayout(LayoutKind.Explicit)] internal struct DoubleConverter { [FieldOffset(0)] public byte Byte0; [FieldOffset(1)] public byte Byte1; [FieldOffset(2)] public byte Byte2; [FieldOffset(3)] public byte Byte3; [FieldOffset(4)] public byte Byte4; [FieldOffset(5)] public byte Byte5; [FieldOffset(6)] public byte Byte6; [FieldOffset(7)] public byte Byte7; [FieldOffset(0)] public double DoubleValue; [FieldOffset(0)] public ulong ULongValue; } }