您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
211 行
7.9 KiB
211 行
7.9 KiB
using Unity.Sample.Core;
|
|
using UnityEngine;
|
|
|
|
namespace NetworkCompression
|
|
{
|
|
public static class NetworkCompressionUtils
|
|
{
|
|
public static int CalculateBucket(uint value)
|
|
{
|
|
int bucketIndex = 0;
|
|
while (bucketIndex + 1 < NetworkCompressionConstants.k_NumBuckets && value >= NetworkCompressionConstants.k_BucketOffsets[bucketIndex + 1]) // TODO: use CountLeadingZeros to do this in constant time
|
|
bucketIndex++;
|
|
|
|
return bucketIndex;
|
|
}
|
|
|
|
public static int CountLeadingZeros(uint value)
|
|
{
|
|
int n = 0;
|
|
while (value != 0)
|
|
{
|
|
value >>= 1;
|
|
n++;
|
|
}
|
|
return 32 - n;
|
|
}
|
|
|
|
public static int CalculateNumGammaBits(long value)
|
|
{
|
|
int numOutputBits = 1;
|
|
int numPrefixBits = 0;
|
|
while (value >= (1L << numOutputBits)) // RUTODO: Unroll this and merge with bit output. How do we actually verify inlining in C#?
|
|
{
|
|
value -= (1L << numOutputBits);
|
|
numOutputBits += 2;
|
|
numPrefixBits++;
|
|
}
|
|
return numPrefixBits + 1 + numOutputBits;
|
|
}
|
|
|
|
public static void GenerateLengthLimitedHuffmanCodeLengths(byte[] symbolLengths, int symbolLengthsOffset, int[] symbolFrequencies, int alphabetSize, int maxCodeLength)
|
|
{
|
|
GameDebug.Assert(alphabetSize <= 255);
|
|
GameDebug.Assert(maxCodeLength <= 8);
|
|
|
|
var sortEntries = new SortEntry[alphabetSize];
|
|
int lastNonZeroIndex = 0;
|
|
int numSortEntries = 0;
|
|
for (int symbol = 0; symbol < alphabetSize; symbol++)
|
|
{
|
|
symbolLengths[symbol + symbolLengthsOffset] = 0;
|
|
|
|
int frequency = symbolFrequencies[symbol];
|
|
if (frequency > 0)
|
|
{
|
|
lastNonZeroIndex = symbol;
|
|
sortEntries[numSortEntries].frequency = frequency;
|
|
sortEntries[numSortEntries].symbol = (byte)symbol;
|
|
numSortEntries++;
|
|
}
|
|
}
|
|
|
|
if (numSortEntries == 1)
|
|
{
|
|
symbolLengths[lastNonZeroIndex] = 1;
|
|
return;
|
|
}
|
|
|
|
System.Array.Resize(ref sortEntries, numSortEntries);
|
|
System.Array.Sort(sortEntries, (a, b) => a.frequency - b.frequency);
|
|
|
|
int numNodes = alphabetSize * maxCodeLength * 2;
|
|
var nodes = new Node[numNodes];
|
|
for (int i = 0; i < numNodes; i++)
|
|
nodes[i] = new Node();
|
|
|
|
int nodesPointer = 0;
|
|
int numPrevNodes = 0;
|
|
int prevNodesPointer = 0;
|
|
for (int length = 1; length <= maxCodeLength; length++)
|
|
{
|
|
int num_a = numPrevNodes;
|
|
int num_b = numSortEntries;
|
|
|
|
int aPointer = prevNodesPointer;
|
|
prevNodesPointer = nodesPointer;
|
|
numPrevNodes = 0;
|
|
while (num_a >= 2 || num_b > 0)
|
|
{
|
|
if (num_b > 0 && (num_a < 2 || sortEntries[numSortEntries - num_b].frequency <= nodes[aPointer + 0].frequency + nodes[aPointer + 1].frequency))
|
|
{
|
|
var e = sortEntries[numSortEntries - num_b];
|
|
var node = nodes[nodesPointer++];
|
|
node.frequency = e.frequency;
|
|
node.symbol = e.symbol;
|
|
num_b--;
|
|
}
|
|
else
|
|
{
|
|
var node = nodes[nodesPointer++];
|
|
node.frequency = nodes[aPointer + 0].frequency + nodes[aPointer + 1].frequency;
|
|
node.symbol = 0xFF;
|
|
node.leftChild = nodes[aPointer + 0];
|
|
node.rightChild = nodes[aPointer + 1];
|
|
num_a -= 2;
|
|
aPointer += 2;
|
|
}
|
|
numPrevNodes++;
|
|
}
|
|
}
|
|
|
|
int numActive = 2 * numSortEntries - 2;
|
|
for (int i = 0; i < numActive; i++)
|
|
{
|
|
GenerateLengthLimitedHuffmanCodeLengthsRecursive(nodes[prevNodesPointer + i], symbolLengths);
|
|
}
|
|
}
|
|
|
|
public static void GenerateHuffmanCodes(byte[] symboLCodes, int symbolCodesOffset, byte[] symbolLengths, int symbolLengthsOffset, int alphabetSize, int maxCodeLength)
|
|
{
|
|
GameDebug.Assert(alphabetSize <= 256);
|
|
GameDebug.Assert(maxCodeLength <= 8);
|
|
|
|
var lengthCounts = new byte[maxCodeLength + 1];
|
|
var symbolList = new byte[maxCodeLength + 1, alphabetSize];
|
|
|
|
//byte[] symbol_list[(MAX_HUFFMAN_CODE_LENGTH + 1u) * MAX_NUM_HUFFMAN_SYMBOLS];
|
|
for (int symbol = 0; symbol < alphabetSize; symbol++)
|
|
{
|
|
int length = symbolLengths[symbol + symbolLengthsOffset];
|
|
GameDebug.Assert(length <= maxCodeLength);
|
|
symbolList[length, lengthCounts[length]++] = (byte)symbol;
|
|
}
|
|
|
|
uint nextCodeWord = 0;
|
|
for (int length = 1; length <= maxCodeLength; length++)
|
|
{
|
|
int length_count = lengthCounts[length];
|
|
for (int i = 0; i < length_count; i++)
|
|
{
|
|
int symbol = symbolList[length, i];
|
|
GameDebug.Assert(symbolLengths[symbol + symbolLengthsOffset] == length);
|
|
symboLCodes[symbol + symbolCodesOffset] = (byte)ReverseBits(nextCodeWord++, length);
|
|
}
|
|
nextCodeWord <<= 1;
|
|
}
|
|
}
|
|
|
|
// decode table entries: (symbol << 8) | length
|
|
public static void GenerateHuffmanDecodeTable(ushort[] decodeTable, int decodeTableOffset, byte[] symbolLengths, byte[] symbolCodes, int alphabetSize, int maxCodeLength)
|
|
{
|
|
GameDebug.Assert(alphabetSize <= 256);
|
|
GameDebug.Assert(maxCodeLength <= 8);
|
|
|
|
uint maxCode = 1u << maxCodeLength;
|
|
for (int symbol = 0; symbol < alphabetSize; symbol++)
|
|
{
|
|
int length = symbolLengths[symbol];
|
|
GameDebug.Assert(length <= maxCodeLength);
|
|
if (length > 0)
|
|
{
|
|
uint code = symbolCodes[symbol];
|
|
uint step = 1u << length;
|
|
do
|
|
{
|
|
decodeTable[decodeTableOffset + code] = (ushort)(symbol << 8 | length);
|
|
code += step;
|
|
}
|
|
while (code < maxCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static uint ReverseBits(uint value, int num_bits)
|
|
{
|
|
value = ((value & 0x55555555u) << 1) | ((value & 0xAAAAAAAAu) >> 1);
|
|
value = ((value & 0x33333333u) << 2) | ((value & 0xCCCCCCCCu) >> 2);
|
|
value = ((value & 0x0F0F0F0Fu) << 4) | ((value & 0xF0F0F0F0u) >> 4);
|
|
value = ((value & 0x00FF00FFu) << 8) | ((value & 0xFF00FF00u) >> 8);
|
|
value = (value << 16) | (value >> 16);
|
|
return value >> (32 - num_bits);
|
|
}
|
|
|
|
private class Node
|
|
{
|
|
public byte symbol;
|
|
public int frequency;
|
|
public Node leftChild;
|
|
public Node rightChild;
|
|
};
|
|
|
|
private struct SortEntry
|
|
{
|
|
public byte symbol;
|
|
public int frequency;
|
|
};
|
|
|
|
private static void GenerateLengthLimitedHuffmanCodeLengthsRecursive(Node node, byte[] symbolLengths)
|
|
{
|
|
if (node.symbol == 0xFF)
|
|
{
|
|
GenerateLengthLimitedHuffmanCodeLengthsRecursive(node.leftChild, symbolLengths);
|
|
GenerateLengthLimitedHuffmanCodeLengthsRecursive(node.rightChild, symbolLengths);
|
|
}
|
|
else
|
|
{
|
|
symbolLengths[node.symbol]++;
|
|
}
|
|
}
|
|
}
|
|
}
|