using System.Runtime.CompilerServices; namespace Unity.Netcode { /// /// Utility class to count the number of bytes or bits needed to serialize a value. /// public static class BitCounter { // Since we don't have access to BitOperations.LeadingZeroCount() (which would have been the fastest) // we use the De Bruijn sequence to do this calculation // See https://en.wikipedia.org/wiki/De_Bruijn_sequence and https://www.chessprogramming.org/De_Bruijn_Sequence private const ulong k_DeBruijnMagic64 = 0x37E84A99DAE458F; private const uint k_DeBruijnMagic32 = 0x06EB14F9; // We're counting bytes, not bits, so these have all had the operation x/8 + 1 applied private static readonly int[] k_DeBruijnTableBytes64 = { 0/8+1, 1/8+1, 17/8+1, 2/8+1, 18/8+1, 50/8+1, 3/8+1, 57/8+1, 47/8+1, 19/8+1, 22/8+1, 51/8+1, 29/8+1, 4/8+1, 33/8+1, 58/8+1, 15/8+1, 48/8+1, 20/8+1, 27/8+1, 25/8+1, 23/8+1, 52/8+1, 41/8+1, 54/8+1, 30/8+1, 38/8+1, 5/8+1, 43/8+1, 34/8+1, 59/8+1, 8/8+1, 63/8+1, 16/8+1, 49/8+1, 56/8+1, 46/8+1, 21/8+1, 28/8+1, 32/8+1, 14/8+1, 26/8+1, 24/8+1, 40/8+1, 53/8+1, 37/8+1, 42/8+1, 7/8+1, 62/8+1, 55/8+1, 45/8+1, 31/8+1, 13/8+1, 39/8+1, 36/8+1, 6/8+1, 61/8+1, 44/8+1, 12/8+1, 35/8+1, 60/8+1, 11/8+1, 10/8+1, 9/8+1, }; private static readonly int[] k_DeBruijnTableBytes32 = { 0/8+1, 1/8+1, 16/8+1, 2/8+1, 29/8+1, 17/8+1, 3/8+1, 22/8+1, 30/8+1, 20/8+1, 18/8+1, 11/8+1, 13/8+1, 4/8+1, 7/8+1, 23/8+1, 31/8+1, 15/8+1, 28/8+1, 21/8+1, 19/8+1, 10/8+1, 12/8+1, 6/8+1, 14/8+1, 27/8+1, 9/8+1, 5/8+1, 26/8+1, 8/8+1, 25/8+1, 24/8+1, }; // And here we're counting the number of set bits, not the position of the highest set, // so these still have +1 applied - unfortunately 0 and 1 both return the same value. private static readonly int[] k_DeBruijnTableBits64 = { 0+1, 1+1, 17+1, 2+1, 18+1, 50+1, 3+1, 57+1, 47+1, 19+1, 22+1, 51+1, 29+1, 4+1, 33+1, 58+1, 15+1, 48+1, 20+1, 27+1, 25+1, 23+1, 52+1, 41+1, 54+1, 30+1, 38+1, 5+1, 43+1, 34+1, 59+1, 8+1, 63+1, 16+1, 49+1, 56+1, 46+1, 21+1, 28+1, 32+1, 14+1, 26+1, 24+1, 40+1, 53+1, 37+1, 42+1, 7+1, 62+1, 55+1, 45+1, 31+1, 13+1, 39+1, 36+1, 6+1, 61+1, 44+1, 12+1, 35+1, 60+1, 11+1, 10+1, 9+1, }; private static readonly int[] k_DeBruijnTableBits32 = { 0+1, 1+1, 16+1, 2+1, 29+1, 17+1, 3+1, 22+1, 30+1, 20+1, 18+1, 11+1, 13+1, 4+1, 7+1, 23+1, 31+1, 15+1, 28+1, 21+1, 19+1, 10+1, 12+1, 6+1, 14+1, 27+1, 9+1, 5+1, 26+1, 8+1, 25+1, 24+1, }; /// /// Get the minimum number of bytes required to represent the given value /// /// The value /// The number of bytes required [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetUsedByteCount(uint value) { value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; value = value & ~(value >> 1); return k_DeBruijnTableBytes32[value * k_DeBruijnMagic32 >> 27]; } /// /// Get the minimum number of bytes required to represent the given value /// /// The value /// The number of bytes required [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetUsedByteCount(ulong value) { value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; value |= value >> 32; value = value & ~(value >> 1); return k_DeBruijnTableBytes64[value * k_DeBruijnMagic64 >> 58]; } /// /// Get the minimum number of bits required to represent the given value /// /// The value /// The number of bits required [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetUsedBitCount(uint value) { value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; value = value & ~(value >> 1); return k_DeBruijnTableBits32[value * k_DeBruijnMagic32 >> 27]; } /// /// Get the minimum number of bits required to represent the given value /// /// The value /// The number of bits required [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetUsedBitCount(ulong value) { value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; value |= value >> 32; value = value & ~(value >> 1); return k_DeBruijnTableBits64[value * k_DeBruijnMagic64 >> 58]; } } }