mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 11:00:32 +00:00
feature: Utils.BitsRequired to prepare for BitTree delta compression
This commit is contained in:
parent
706bbaa3b0
commit
339a9d9967
@ -80,6 +80,106 @@ public static int RoundBitsToFullBytes(int bits)
|
||||
return ((bits - 1) / 8) + 1;
|
||||
}
|
||||
|
||||
// calculate bits needed for a value range
|
||||
// largest type we support is ulong, so use that as parameters
|
||||
// min, max are both INCLUSIVE
|
||||
// min=0, max=7 means 0..7 = 8 values in total = 3 bits required
|
||||
public static int BitsRequired(ulong min, ulong max)
|
||||
{
|
||||
// make sure value is within range
|
||||
// => throws exception because the developer should fix it immediately
|
||||
if (min > max)
|
||||
throw new ArgumentOutOfRangeException($"{nameof(BitsRequired)} min={min} needs to be <= max={max}");
|
||||
|
||||
// if min == max then we need 0 bits because it is only ever one value
|
||||
if (min == max)
|
||||
return 0;
|
||||
|
||||
// normalize from min..max to 0..max-min
|
||||
// example:
|
||||
// min = 0, max = 7 => 7-0 = 7 (0..7 = 8 values needed)
|
||||
// min = 4, max = 7 => 7-4 = 3 (0..3 = 4 values needed)
|
||||
//
|
||||
// CAREFUL: DO NOT ADD ANYTHING TO THIS VALUE.
|
||||
// if min=0 and max=ulong.max then normalized = ulong.max,
|
||||
// adding anything to it would make it overflow!
|
||||
// (see tests!)
|
||||
ulong normalized = max - min;
|
||||
//UnityEngine.Debug.Log($"min={min} max={max} normalized={normalized}");
|
||||
|
||||
// .Net Core 3.1 has BitOperations.Log2(x)
|
||||
// Unity doesn't, so we could use one of a dozen weird tricks:
|
||||
// https://stackoverflow.com/questions/15967240/fastest-implementation-of-log2int-and-log2float
|
||||
// including lookup tables, float exponent tricks for little endian,
|
||||
// etc.
|
||||
//
|
||||
// ... or we could just hard code!
|
||||
if (normalized < 2) return 1;
|
||||
if (normalized < 4) return 2;
|
||||
if (normalized < 8) return 3;
|
||||
if (normalized < 16) return 4;
|
||||
if (normalized < 32) return 5;
|
||||
if (normalized < 64) return 6;
|
||||
if (normalized < 128) return 7;
|
||||
if (normalized < 256) return 8;
|
||||
if (normalized < 512) return 9;
|
||||
if (normalized < 1024) return 10;
|
||||
if (normalized < 2048) return 11;
|
||||
if (normalized < 4096) return 12;
|
||||
if (normalized < 8192) return 13;
|
||||
if (normalized < 16384) return 14;
|
||||
if (normalized < 32768) return 15;
|
||||
if (normalized < 65536) return 16;
|
||||
if (normalized < 131072) return 17;
|
||||
if (normalized < 262144) return 18;
|
||||
if (normalized < 524288) return 19;
|
||||
if (normalized < 1048576) return 20;
|
||||
if (normalized < 2097152) return 21;
|
||||
if (normalized < 4194304) return 22;
|
||||
if (normalized < 8388608) return 23;
|
||||
if (normalized < 16777216) return 24;
|
||||
if (normalized < 33554432) return 25;
|
||||
if (normalized < 67108864) return 26;
|
||||
if (normalized < 134217728) return 27;
|
||||
if (normalized < 268435456) return 28;
|
||||
if (normalized < 536870912) return 29;
|
||||
if (normalized < 1073741824) return 30;
|
||||
if (normalized < 2147483648) return 31;
|
||||
if (normalized < 4294967296) return 32;
|
||||
if (normalized < 8589934592) return 33;
|
||||
if (normalized < 17179869184) return 34;
|
||||
if (normalized < 34359738368) return 35;
|
||||
if (normalized < 68719476736) return 36;
|
||||
if (normalized < 137438953472) return 37;
|
||||
if (normalized < 274877906944) return 38;
|
||||
if (normalized < 549755813888) return 39;
|
||||
if (normalized < 1099511627776) return 40;
|
||||
if (normalized < 2199023255552) return 41;
|
||||
if (normalized < 4398046511104) return 42;
|
||||
if (normalized < 8796093022208) return 43;
|
||||
if (normalized < 17592186044416) return 44;
|
||||
if (normalized < 35184372088832) return 45;
|
||||
if (normalized < 70368744177664) return 46;
|
||||
if (normalized < 140737488355328) return 47;
|
||||
if (normalized < 281474976710656) return 48;
|
||||
if (normalized < 562949953421312) return 49;
|
||||
if (normalized < 1125899906842624) return 50;
|
||||
if (normalized < 2251799813685248) return 51;
|
||||
if (normalized < 4503599627370496) return 52;
|
||||
if (normalized < 9007199254740992) return 53;
|
||||
if (normalized < 18014398509481984) return 54;
|
||||
if (normalized < 36028797018963968) return 55;
|
||||
if (normalized < 72057594037927936) return 56;
|
||||
if (normalized < 144115188075855872) return 57;
|
||||
if (normalized < 288230376151711744) return 58;
|
||||
if (normalized < 576460752303423488) return 59;
|
||||
if (normalized < 1152921504606846976) return 60;
|
||||
if (normalized < 2305843009213693952) return 61;
|
||||
if (normalized < 4611686018427387904) return 62;
|
||||
if (normalized < 9223372036854775808) return 63;
|
||||
return 64;
|
||||
}
|
||||
|
||||
public static bool IsPrefab(GameObject obj)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
@ -64,6 +65,58 @@ public void RoundBitsToFullBytes()
|
||||
Assert.That(Utils.RoundBitsToFullBytes(i), Is.EqualTo(8));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BitsRequired()
|
||||
{
|
||||
// min > max should never work
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Utils.BitsRequired(2, 1));
|
||||
|
||||
// 2..2 is only ever 1 value = 2, so 0 bits needed
|
||||
Assert.That(Utils.BitsRequired(2, 2), Is.EqualTo(0));
|
||||
|
||||
// 3..4 are 2 values, so 1 bit
|
||||
Assert.That(Utils.BitsRequired(3, 4), Is.EqualTo(1));
|
||||
|
||||
// 0..7 are 8 values, so 3 bits
|
||||
Assert.That(Utils.BitsRequired(0, 7), Is.EqualTo(3));
|
||||
|
||||
// 4..7 are 4 values, so 2 bits
|
||||
Assert.That(Utils.BitsRequired(4, 7), Is.EqualTo(2));
|
||||
|
||||
// 0..255 are 256 values, so 8 bits
|
||||
Assert.That(Utils.BitsRequired(0, 255), Is.EqualTo(8));
|
||||
|
||||
// 128..255 are 128 values, so 7 bits
|
||||
Assert.That(Utils.BitsRequired(128, 255), Is.EqualTo(7));
|
||||
|
||||
// ushort should always fit into 2 bytes
|
||||
Assert.That(Utils.BitsRequired(0, ushort.MaxValue), Is.EqualTo(16));
|
||||
|
||||
// uint should always fit into 4 bytes
|
||||
Assert.That(Utils.BitsRequired(0, uint.MaxValue), Is.EqualTo(32));
|
||||
|
||||
// ulong should always fit into 8 bytes
|
||||
// this is the maximum range that ever fits into an ulong.
|
||||
// a lot of things could go wrong, e.g. if we +1 to that range
|
||||
// without being careful.
|
||||
// THIS TEST IS HUGELY IMPORTANT.
|
||||
Assert.That(Utils.BitsRequired(0, ulong.MaxValue), Is.EqualTo(64));
|
||||
|
||||
// since we hardcoded the values, let's test for 1..63 shifted bits
|
||||
// just to be sure that all of the hard coded values are correct!
|
||||
for (int bits = 1; bits < 64; ++bits)
|
||||
{
|
||||
// 1 bits => bitsMax = 1
|
||||
// 2 bits => bitsMax = 2
|
||||
// 3 bits => bitsMax = 4
|
||||
// etc.
|
||||
// so check 0..bitsMax-1
|
||||
ulong bitsMax = 1ul << bits;
|
||||
//UnityEngine.Debug.Log($"testing bitsMax={bitsMax-1} => bits={bits}");
|
||||
Assert.That(Utils.BitsRequired(0, bitsMax - 1), Is.EqualTo(bits));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsPointInScreen()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user