1
0
mirror of https://github.com/chylex/Brotli-Builder.git synced 2024-10-22 08:42:48 +02:00
Brotli-Builder/BrotliLib/Brotli/Components/Header/HuffmanTree.Type.Complex.cs

142 lines
7.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using BrotliLib.Brotli.Parameters;
using BrotliLib.Collections;
using BrotliLib.Collections.Huffman;
using BrotliLib.Markers.Serialization;
using BrotliLib.Serialization;
namespace BrotliLib.Brotli.Components.Header{
partial class HuffmanTree<T>{
/// <summary>
/// https://tools.ietf.org/html/rfc7932#section-3.5
/// </summary>
private static class Complex{
private const int SymbolBitSpace = 1 << DefaultMaxDepth;
private const byte NoForcedCode = byte.MaxValue;
public static readonly BitDeserializer<HuffmanTree<T>, Context> Deserialize = MarkedBitDeserializer.Wrap<HuffmanTree<T>, Context>(
(reader, context) => {
Func<int, T> getSymbol = context.BitsToSymbol;
var defaultRepeatCode = new HuffmanGenerator<T>.Entry(getSymbol(0), HuffmanTreeLengthCode.InitialRepeatedCode);
var lengthCodes = HuffmanTreeLengthCode.Read(reader, context.SkippedComplexCodeLengths);
byte nextForcedCode = NoForcedCode;
int symbolIndex = 0;
int symbolCount = context.AlphabetSize.SymbolCount;
var symbolEntries = new List<HuffmanGenerator<T>.Entry>(symbolCount);
int bitSpaceRemaining = SymbolBitSpace;
reader.MarkStart();
void AddMarkedSymbol(HuffmanGenerator<T>.Entry entry){
symbolEntries.Add(entry);
reader.MarkStart();
reader.MarkEndValue("entry", entry);
}
while(bitSpaceRemaining > 0 && symbolIndex < symbolCount){
byte nextCode;
if (nextForcedCode == NoForcedCode){
nextCode = reader.ReadValue(lengthCodes, "code", code => code.Code);
}
else{
nextCode = nextForcedCode;
nextForcedCode = NoForcedCode;
}
if (nextCode == HuffmanTreeLengthCode.Skip){
reader.MarkStart();
int NextSkipData() => 3 + reader.NextChunk(3, "skip data");
int skipCount = NextSkipData();
while((nextForcedCode = reader.ReadValue(lengthCodes, "code", code => code.Code)) == HuffmanTreeLengthCode.Skip){
skipCount = 8 * (skipCount - 2) + NextSkipData();
}
reader.MarkEndValue("skip count", skipCount);
symbolIndex += skipCount;
}
else if (nextCode == HuffmanTreeLengthCode.Repeat){
byte repeatedCode = symbolEntries.DefaultIfEmpty(defaultRepeatCode).Select(entry => entry.Bits).Last(value => value > 0);
int sumPerRepeat = SymbolBitSpace >> repeatedCode;
reader.MarkStart();
int NextRepeatData() => 3 + reader.NextChunk(2, "repeat data");
int repeatCount = NextRepeatData();
while(bitSpaceRemaining - sumPerRepeat * repeatCount > 0 && (nextForcedCode = reader.ReadValue(lengthCodes, "code", code => code.Code)) == HuffmanTreeLengthCode.Repeat){
repeatCount = 4 * (repeatCount - 2) + NextRepeatData();
}
reader.MarkEndValue("repeat count", repeatCount);
bitSpaceRemaining -= sumPerRepeat * repeatCount;
while(--repeatCount >= 0){
AddMarkedSymbol(new HuffmanGenerator<T>.Entry(getSymbol(symbolIndex++), repeatedCode));
}
}
else if (nextCode == 0){
++symbolIndex;
}
else if (nextCode <= HuffmanTreeLengthCode.MaxLength){
AddMarkedSymbol(new HuffmanGenerator<T>.Entry(getSymbol(symbolIndex++), nextCode));
bitSpaceRemaining -= SymbolBitSpace >> nextCode;
}
}
reader.MarkEndTitle("Symbols");
return new HuffmanTree<T>(HuffmanGenerator<T>.FromBitCountsCanonical(symbolEntries.ToArray()));
}
);
public static readonly BitSerializer<HuffmanTree<T>, Context, BrotliSerializationParameters> Serialize = (writer, obj, context, parameters) => {
Func<int, T> getSymbol = context.BitsToSymbol;
int symbolCount = context.AlphabetSize.SymbolCount;
var symbolLengths = new List<byte>();
for(int symbolIndex = 0, bitSpaceRemaining = SymbolBitSpace; bitSpaceRemaining > 0 && symbolIndex < symbolCount; symbolIndex++){
BitPath? path = obj.FindPathOrNull(getSymbol(symbolIndex));
byte length = path?.Length ?? 0;
if (length > 0){
bitSpaceRemaining -= SymbolBitSpace >> length;
}
else if (symbolIndex == symbolCount - 1){
length = 15; // if the tree is incomplete, a zero length last symbol would generate an ending length code 0 or 17, which Brotli spec forbids
// if the tree is complete and the final symbol was supposed to be a 0, the writer will run out of bit space before it writes the final symbol
}
symbolLengths.Add(length);
}
var (lengthCodes, extra) = parameters.HuffmanTreeRLE(new HuffmanTreeLengthCode.RunDecider(symbolLengths, context.AlphabetSize)).GenerateCodesAndExtraBits();
var lengthCodeMap = parameters.GenerateHuffmanLengthCodeTree(FrequencyList<byte>.FromSequence(lengthCodes), HuffmanTreeLengthCode.LengthMaxDepth).Root.GenerateValueMapOptimized();
HuffmanTreeLengthCode.Write(writer, lengthCodeMap);
foreach(byte code in lengthCodes){
writer.WriteBits(lengthCodeMap[code]);
if (code == HuffmanTreeLengthCode.Skip){
writer.WriteChunk(HuffmanTreeLengthCode.SkipCodeExtraBits, extra.Dequeue());
}
else if (code == HuffmanTreeLengthCode.Repeat){
writer.WriteChunk(HuffmanTreeLengthCode.RepeatCodeExtraBits, extra.Dequeue());
}
}
};
}
}
}