| using System; |
| using Unity.Collections.LowLevel.Unsafe; |
|
|
| namespace UnityEngine.InputSystem.Utilities |
| { |
| internal static unsafe class MemoryHelpers |
| { |
| public struct BitRegion |
| { |
| public uint bitOffset; |
| public uint sizeInBits; |
|
|
| public bool isEmpty => sizeInBits == 0; |
|
|
| public BitRegion(uint bitOffset, uint sizeInBits) |
| { |
| this.bitOffset = bitOffset; |
| this.sizeInBits = sizeInBits; |
| } |
|
|
| public BitRegion(uint byteOffset, uint bitOffset, uint sizeInBits) |
| { |
| this.bitOffset = byteOffset * 8 + bitOffset; |
| this.sizeInBits = sizeInBits; |
| } |
|
|
| public BitRegion Overlap(BitRegion other) |
| { |
| |
|
|
| var thisEnd = bitOffset + sizeInBits; |
| var otherEnd = other.bitOffset + other.sizeInBits; |
|
|
| if (thisEnd <= other.bitOffset || otherEnd <= bitOffset) |
| return default; |
|
|
| var end = Math.Min(thisEnd, otherEnd); |
| var start = Math.Max(bitOffset, other.bitOffset); |
|
|
| return new BitRegion(start, end - start); |
| } |
| } |
|
|
| public static bool Compare(void* ptr1, void* ptr2, BitRegion region) |
| { |
| if (region.sizeInBits == 1) |
| return ReadSingleBit(ptr1, region.bitOffset) == ReadSingleBit(ptr2, region.bitOffset); |
| return MemCmpBitRegion(ptr1, ptr2, region.bitOffset, region.sizeInBits); |
| } |
|
|
| public static uint ComputeFollowingByteOffset(uint byteOffset, uint sizeInBits) |
| { |
| return (uint)(byteOffset + sizeInBits / 8 + (sizeInBits % 8 > 0 ? 1 : 0)); |
| } |
|
|
| public static void WriteSingleBit(void* ptr, uint bitOffset, bool value) |
| { |
| var byteOffset = bitOffset >> 3; |
| bitOffset &= 7; |
| if (value) |
| *((byte*)ptr + byteOffset) |= (byte)(1U << (int)bitOffset); |
| else |
| *((byte*)ptr + byteOffset) &= (byte)~(1U << (int)bitOffset); |
| } |
|
|
| public static bool ReadSingleBit(void* ptr, uint bitOffset) |
| { |
| var byteOffset = bitOffset >> 3; |
| bitOffset &= 7; |
| return (*((byte*)ptr + byteOffset) & (1U << (int)bitOffset)) != 0; |
| } |
|
|
| public static void MemCpyBitRegion(void* destination, void* source, uint bitOffset, uint bitCount) |
| { |
| var destPtr = (byte*)destination; |
| var sourcePtr = (byte*)source; |
|
|
| |
| if (bitOffset >= 8) |
| { |
| var skipBytes = bitOffset / 8; |
| destPtr += skipBytes; |
| sourcePtr += skipBytes; |
| bitOffset %= 8; |
| } |
|
|
| |
| if (bitOffset > 0) |
| { |
| var byteMask = 0xFF << (int)bitOffset; |
| if (bitCount + bitOffset < 8) |
| byteMask &= 0xFF >> (int)(8 - (bitCount + bitOffset)); |
|
|
| *destPtr = (byte)(((*destPtr & ~byteMask) | (*sourcePtr & byteMask)) & 0xFF); |
|
|
| |
| |
| if (bitCount + bitOffset <= 8) |
| return; |
|
|
| ++destPtr; |
| ++sourcePtr; |
|
|
| bitCount -= 8 - bitOffset; |
| } |
|
|
| |
| var byteCount = bitCount / 8; |
| if (byteCount >= 1) |
| UnsafeUtility.MemCpy(destPtr, sourcePtr, byteCount); |
|
|
| |
| var remainingBitCount = bitCount % 8; |
| if (remainingBitCount > 0) |
| { |
| destPtr += byteCount; |
| sourcePtr += byteCount; |
|
|
| |
| var byteMask = 0xFF >> (int)(8 - remainingBitCount); |
|
|
| *destPtr = (byte)(((*destPtr & ~byteMask) | (*sourcePtr & byteMask)) & 0xFF); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static bool MemCmpBitRegion(void* ptr1, void* ptr2, uint bitOffset, uint bitCount, void* mask = null) |
| { |
| var bytePtr1 = (byte*)ptr1; |
| var bytePtr2 = (byte*)ptr2; |
| var maskPtr = (byte*)mask; |
|
|
| |
| if (bitOffset >= 8) |
| { |
| var skipBytes = bitOffset / 8; |
| bytePtr1 += skipBytes; |
| bytePtr2 += skipBytes; |
| if (maskPtr != null) |
| maskPtr += skipBytes; |
| bitOffset %= 8; |
| } |
|
|
| |
| if (bitOffset > 0) |
| { |
| |
| |
| var byteMask = 0xFF << (int)bitOffset; |
| if (bitCount + bitOffset < 8) |
| byteMask &= 0xFF >> (int)(8 - (bitCount + bitOffset)); |
|
|
| if (maskPtr != null) |
| { |
| byteMask &= *maskPtr; |
| ++maskPtr; |
| } |
|
|
| var byte1 = *bytePtr1 & byteMask; |
| var byte2 = *bytePtr2 & byteMask; |
|
|
| if (byte1 != byte2) |
| return false; |
|
|
| |
| |
| if (bitCount + bitOffset <= 8) |
| return true; |
|
|
| ++bytePtr1; |
| ++bytePtr2; |
|
|
| bitCount -= 8 - bitOffset; |
| } |
|
|
| |
| var byteCount = bitCount / 8; |
| if (byteCount >= 1) |
| { |
| if (maskPtr != null) |
| { |
| |
| |
| for (var i = 0; i < byteCount; ++i) |
| { |
| var byte1 = bytePtr1[i]; |
| var byte2 = bytePtr2[i]; |
| var byteMask = maskPtr[i]; |
|
|
| if ((byte1 & byteMask) != (byte2 & byteMask)) |
| return false; |
| } |
| } |
| else |
| { |
| if (UnsafeUtility.MemCmp(bytePtr1, bytePtr2, byteCount) != 0) |
| return false; |
| } |
| } |
|
|
| |
| var remainingBitCount = bitCount % 8; |
| if (remainingBitCount > 0) |
| { |
| bytePtr1 += byteCount; |
| bytePtr2 += byteCount; |
|
|
| |
| var byteMask = 0xFF >> (int)(8 - remainingBitCount); |
|
|
| if (maskPtr != null) |
| { |
| maskPtr += byteCount; |
| byteMask &= *maskPtr; |
| } |
|
|
| var byte1 = *bytePtr1 & byteMask; |
| var byte2 = *bytePtr2 & byteMask; |
|
|
| if (byte1 != byte2) |
| return false; |
| } |
|
|
| return true; |
| } |
|
|
| public static void MemSet(void* destination, int numBytes, byte value) |
| { |
| var to = (byte*)destination; |
| var pos = 0; |
|
|
| unchecked |
| { |
| |
| #if UNITY_64 |
| while (numBytes >= 8) |
| { |
| *(ulong*)&to[pos] = ((ulong)value << 56) | ((ulong)value << 48) | ((ulong)value << 40) | ((ulong)value << 32) |
| | ((ulong)value << 24) | ((ulong)value << 16) | ((ulong)value << 8) | value; |
| numBytes -= 8; |
| pos += 8; |
| } |
| #endif |
|
|
| |
| while (numBytes >= 4) |
| { |
| *(uint*)&to[pos] = ((uint)value << 24) | ((uint)value << 16) | ((uint)value << 8) | value; |
| numBytes -= 4; |
| pos += 4; |
| } |
|
|
| |
| while (numBytes > 0) |
| { |
| to[pos] = value; |
| numBytes -= 1; |
| pos += 1; |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public static void MemCpyMasked(void* destination, void* source, int numBytes, void* mask) |
| { |
| var from = (byte*)source; |
| var to = (byte*)destination; |
| var bits = (byte*)mask; |
| var pos = 0; |
|
|
| unchecked |
| { |
| |
| #if UNITY_64 |
| while (numBytes >= 8) |
| { |
| *(ulong*)(to + pos) &= ~*(ulong*)(bits + pos); |
| *(ulong*)(to + pos) |= *(ulong*)(from + pos) & *(ulong*)(bits + pos); |
| numBytes -= 8; |
| pos += 8; |
| } |
| #endif |
|
|
| |
| while (numBytes >= 4) |
| { |
| *(uint*)(to + pos) &= ~*(uint*)(bits + pos); |
| *(uint*)(to + pos) |= *(uint*)(from + pos) & *(uint*)(bits + pos); |
| numBytes -= 4; |
| pos += 4; |
| } |
|
|
| |
| while (numBytes > 0) |
| { |
| unchecked |
| { |
| to[pos] &= (byte)~bits[pos]; |
| to[pos] |= (byte)(from[pos] & bits[pos]); |
| } |
| numBytes -= 1; |
| pos += 1; |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public static uint ReadMultipleBitsAsUInt(void* ptr, uint bitOffset, uint bitCount) |
| { |
| if (ptr == null) |
| throw new ArgumentNullException(nameof(ptr)); |
| if (bitCount > sizeof(int) * 8) |
| throw new ArgumentException("Trying to read more than 32 bits as int", nameof(bitCount)); |
|
|
| |
| if (bitOffset > 32) |
| { |
| var newBitOffset = (int)bitOffset % 32; |
| var intOffset = ((int)bitOffset - newBitOffset) / 32; |
| ptr = (byte*)ptr + (intOffset * 4); |
| bitOffset = (uint)newBitOffset; |
| } |
|
|
| |
| if (bitOffset + bitCount <= 8) |
| { |
| var value = *(byte*)ptr; |
| value >>= (int)bitOffset; |
| var mask = 0xFFu >> (8 - (int)bitCount); |
| return value & mask; |
| } |
|
|
| |
| if (bitOffset + bitCount <= 16) |
| { |
| var value = *(ushort*)ptr; |
| value >>= (int)bitOffset; |
| var mask = 0xFFFFu >> (16 - (int)bitCount); |
| return value & mask; |
| } |
|
|
| |
| if (bitOffset + bitCount <= 32) |
| { |
| var value = *(uint*)ptr; |
| value >>= (int)bitOffset; |
| var mask = 0xFFFFFFFFu >> (32 - (int)bitCount); |
| return value & mask; |
| } |
|
|
| throw new NotImplementedException("Reading int straddling int boundary"); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public static void WriteUIntAsMultipleBits(void* ptr, uint bitOffset, uint bitCount, uint value) |
| { |
| if (ptr == null) |
| throw new ArgumentNullException(nameof(ptr)); |
| if (bitCount > sizeof(int) * 8) |
| throw new ArgumentException("Trying to write more than 32 bits as int", nameof(bitCount)); |
|
|
| |
| if (bitOffset > 32) |
| { |
| var newBitOffset = (int)bitOffset % 32; |
| var intOffset = ((int)bitOffset - newBitOffset) / 32; |
| ptr = (byte*)ptr + (intOffset * 4); |
| bitOffset = (uint)newBitOffset; |
| } |
|
|
| |
| if (bitOffset + bitCount <= 8) |
| { |
| var byteValue = (byte)value; |
| byteValue <<= (int)bitOffset; |
| var mask = ~((0xFFU >> (8 - (int)bitCount)) << (int)bitOffset); |
| *(byte*)ptr = (byte)((*(byte*)ptr & mask) | byteValue); |
| return; |
| } |
|
|
| |
| if (bitOffset + bitCount <= 16) |
| { |
| var ushortValue = (ushort)value; |
| ushortValue <<= (int)bitOffset; |
| var mask = ~((0xFFFFU >> (16 - (int)bitCount)) << (int)bitOffset); |
| *(ushort*)ptr = (ushort)((*(ushort*)ptr & mask) | ushortValue); |
| return; |
| } |
|
|
| |
| if (bitOffset + bitCount <= 32) |
| { |
| var uintValue = (uint)value; |
| uintValue <<= (int)bitOffset; |
| var mask = ~((0xFFFFFFFFU >> (32 - (int)bitCount)) << (int)bitOffset); |
| *(uint*)ptr = (*(uint*)ptr & mask) | uintValue; |
| return; |
| } |
|
|
| throw new NotImplementedException("Writing int straddling int boundary"); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public static int ReadTwosComplementMultipleBitsAsInt(void* ptr, uint bitOffset, uint bitCount) |
| { |
| |
| return (int)ReadMultipleBitsAsUInt(ptr, bitOffset, bitCount); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public static void WriteIntAsTwosComplementMultipleBits(void* ptr, uint bitOffset, uint bitCount, int value) |
| { |
| |
| WriteUIntAsMultipleBits(ptr, bitOffset, bitCount, (uint)value); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public static int ReadExcessKMultipleBitsAsInt(void* ptr, uint bitOffset, uint bitCount) |
| { |
| |
| var value = (long)ReadMultipleBitsAsUInt(ptr, bitOffset, bitCount); |
| var halfMax = (long)((1UL << (int)bitCount) / 2); |
| return (int)(value - halfMax); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public static void WriteIntAsExcessKMultipleBits(void* ptr, uint bitOffset, uint bitCount, int value) |
| { |
| |
| var halfMax = (long)((1UL << (int)bitCount) / 2); |
| var unsignedValue = halfMax + value; |
| WriteUIntAsMultipleBits(ptr, bitOffset, bitCount, (uint)unsignedValue); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public static float ReadMultipleBitsAsNormalizedUInt(void* ptr, uint bitOffset, uint bitCount) |
| { |
| var uintValue = ReadMultipleBitsAsUInt(ptr, bitOffset, bitCount); |
| var maxValue = (uint)((1UL << (int)bitCount) - 1); |
| return NumberHelpers.UIntToNormalizedFloat(uintValue, 0, maxValue); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public static void WriteNormalizedUIntAsMultipleBits(void* ptr, uint bitOffset, uint bitCount, float value) |
| { |
| var maxValue = (uint)((1UL << (int)bitCount) - 1); |
| var uintValue = NumberHelpers.NormalizedFloatToUInt(value, 0, maxValue); |
| WriteUIntAsMultipleBits(ptr, bitOffset, bitCount, uintValue); |
| } |
|
|
| public static void SetBitsInBuffer(void* buffer, int byteOffset, int bitOffset, int sizeInBits, bool value) |
| { |
| if (buffer == null) |
| throw new ArgumentException("A buffer must be provided to apply the bitmask on", nameof(buffer)); |
| if (sizeInBits < 0) |
| throw new ArgumentException("Negative sizeInBits", nameof(sizeInBits)); |
| if (bitOffset < 0) |
| throw new ArgumentException("Negative bitOffset", nameof(bitOffset)); |
| if (byteOffset < 0) |
| throw new ArgumentException("Negative byteOffset", nameof(byteOffset)); |
|
|
| |
| if (bitOffset >= 8) |
| { |
| var skipBytes = bitOffset / 8; |
| byteOffset += skipBytes; |
| bitOffset %= 8; |
| } |
|
|
| var bytePos = (byte*)buffer + byteOffset; |
| var sizeRemainingInBits = sizeInBits; |
|
|
| |
| if (bitOffset != 0) |
| { |
| var mask = 0xFF << bitOffset; |
| if (sizeRemainingInBits + bitOffset < 8) |
| { |
| mask &= 0xFF >> (8 - (sizeRemainingInBits + bitOffset)); |
| } |
|
|
| if (value) |
| *bytePos |= (byte)mask; |
| else |
| *bytePos &= (byte)~mask; |
| ++bytePos; |
| sizeRemainingInBits -= 8 - bitOffset; |
| } |
|
|
| |
| while (sizeRemainingInBits >= 8) |
| { |
| *bytePos = value ? (byte)0xFF : (byte)0; |
| ++bytePos; |
| sizeRemainingInBits -= 8; |
| } |
|
|
| |
| if (sizeRemainingInBits > 0) |
| { |
| var mask = (byte)(0xFF >> 8 - sizeRemainingInBits); |
| if (value) |
| *bytePos |= mask; |
| else |
| *bytePos &= (byte)~mask; |
| } |
|
|
| Debug.Assert(bytePos <= (byte*)buffer + |
| ComputeFollowingByteOffset((uint)byteOffset, (uint)bitOffset + (uint)sizeInBits)); |
| } |
|
|
| public static void Swap<TValue>(ref TValue a, ref TValue b) |
| { |
| var temp = a; |
| a = b; |
| b = temp; |
| } |
|
|
| public static uint AlignNatural(uint offset, uint sizeInBytes) |
| { |
| var alignment = Math.Min(8, sizeInBytes); |
| return offset.AlignToMultipleOf(alignment); |
| } |
| } |
| } |
|
|