1
0
mirror of https://github.com/chylex/Brotli-Builder.git synced 2025-05-14 02:34:05 +02:00

Rewrite BlockSwitchTracker

This commit is contained in:
chylex 2020-04-06 20:58:09 +02:00
parent 777f6de11f
commit 4cdf3825d2
4 changed files with 80 additions and 73 deletions

View File

@ -55,13 +55,13 @@ namespace BrotliLib.Brotli.Components.Compressed{
}
internal abstract class DataContext : Context{
public abstract CategoryMap<BlockSwitchTracker> BlockTrackers { get; }
public bool NeedsMoreData => bytesWritten < DataLength.UncompressedBytes;
private int bytesWritten;
protected DataContext(Context wrapped) : base(wrapped.Header, wrapped.DataLength, wrapped.State){}
public abstract int NextBlockID(Category category);
public void WriteLiteral(in Literal literal){
State.OutputLiteral(literal);
++bytesWritten;
@ -99,32 +99,22 @@ namespace BrotliLib.Brotli.Components.Compressed{
}
private class ReaderDataContext : DataContext{
public BlockSwitchCommandMutableMap BlockSwitchCommands => blockTrackers.Select(tracker => tracker.ReadCommands);
private readonly IMarkedBitReader reader;
private readonly CategoryMap<BlockSwitchTracker.Reading> blockTrackers;
public BlockSwitchCommandMutableMap BlockSwitchCommands => readingTrackers.Select(tracker => tracker.ReadCommands);
public override CategoryMap<BlockSwitchTracker> BlockTrackers { get; }
private readonly CategoryMap<BlockSwitchTracker.Reading> readingTrackers;
public ReaderDataContext(Context wrapped, IMarkedBitReader reader) : base(wrapped){
this.reader = reader;
this.blockTrackers = Header.BlockTypes.Select(info => new BlockSwitchTracker.Reading(info));
}
public override int NextBlockID(Category category){
return blockTrackers[category].ReadCommand(reader);
this.readingTrackers = Header.BlockTypes.Select(info => new BlockSwitchTracker.Reading(info, reader));
this.BlockTrackers = readingTrackers.Select<BlockSwitchTracker>(tracker => tracker);
}
}
private class WriterDataContext : DataContext{
private readonly IBitWriter writer;
private readonly CategoryMap<BlockSwitchTracker.Writing> blockTrackers;
public override CategoryMap<BlockSwitchTracker> BlockTrackers { get; }
public WriterDataContext(Context wrapped, BlockSwitchCommandMap blockSwitchCommands, IBitWriter writer) : base(wrapped){
this.writer = writer;
this.blockTrackers = Header.BlockTypes.Select(info => new BlockSwitchTracker.Writing(info, new Queue<BlockSwitchCommand>(blockSwitchCommands[info.Category])));
}
public override int NextBlockID(Category category){
return blockTrackers[category].WriteCommand(writer);
this.BlockTrackers = Header.BlockTypes.Select<BlockSwitchTracker>(info => new BlockSwitchTracker.Writing(info, writer, blockSwitchCommands[info.Category]));
}
}

View File

@ -6,75 +6,88 @@ using BrotliLib.Serialization.Writer;
namespace BrotliLib.Brotli.Components.Data{
/// <summary>
/// Tracks the current block type. Its subtypes <see cref="Reading"/> and <see cref="Writing"/> allow reading and writing block-switch commands.
/// Tracks the current block type.
/// </summary>
public abstract class BlockSwitchTracker{
protected readonly BlockSwitchCommand.Context? context;
protected int currentID;
protected int remaining;
private int currentID;
private int remaining;
protected BlockSwitchTracker(BlockTypeInfo info){
this.context = info.TypeCount == 1 ? null : new BlockSwitchCommand.Context(info, new BlockTypeTracker(info.TypeCount));
this.remaining = info.InitialLength;
}
protected void UpdateState(BlockSwitchCommand command){
currentID = command.Type;
remaining = command.Length;
}
protected abstract BlockSwitchCommand GetNextCommand();
/// <summary>
/// Requests a block-switch command if there are no more symbols in the current block type, then decreases the amount of remaining symbols. Returns <see cref="BlockSwitchTracker.currentID"/>.
/// </summary>
public int Advance(){
if (remaining == 0){
var nextCommand = GetNextCommand();
currentID = nextCommand.Type;
remaining = nextCommand.Length;
}
--remaining;
return currentID;
}
/// <summary>
/// Tracks the current block type, reading commands from an <see cref="IBitReader"/> whenever the current block ends.
/// </summary>
public sealed class Reading : BlockSwitchTracker{
public IList<BlockSwitchCommand> ReadCommands { get; } = new List<BlockSwitchCommand>();
public Reading(BlockTypeInfo info) : base(info){}
private readonly IBitReader reader;
/// <summary>
/// Reads a block-switch command if there are no more symbols in the current block type, then decreases the amount of remaining symbols. Returns <see cref="BlockSwitchTracker.currentID"/>.
/// </summary>
public int ReadCommand(IBitReader reader){
if (remaining == 0){
BlockSwitchCommand nextCommand = BlockSwitchCommand.Deserialize(reader, context!);
ReadCommands.Add(nextCommand);
UpdateState(nextCommand);
}
public Reading(BlockTypeInfo info, IBitReader reader) : base(info){
this.reader = reader;
}
--remaining;
return currentID;
protected override BlockSwitchCommand GetNextCommand(){
var nextCommand = BlockSwitchCommand.Deserialize(reader, context!);
ReadCommands.Add(nextCommand);
return nextCommand;
}
}
/// <summary>
/// Tracks the current block type, writing commands from a list into an <see cref="IBitWriter"/> whenever the current block ends.
/// </summary>
public sealed class Writing : BlockSwitchTracker{
private readonly Queue<BlockSwitchCommand> queue;
private readonly IBitWriter writer;
private readonly IReadOnlyList<BlockSwitchCommand> queue;
private int index;
public Writing(BlockTypeInfo info, Queue<BlockSwitchCommand> queue) : base(info){
public Writing(BlockTypeInfo info, IBitWriter writer, IReadOnlyList<BlockSwitchCommand> queue) : base(info){
this.writer = writer;
this.queue = queue;
}
/// <summary>
/// Writes a block-switch command if there are no more symbols in the current block type, then decreases the amount of remaining symbols. Returns <see cref="BlockSwitchTracker.currentID"/>.
/// </summary>
public int WriteCommand(IBitWriter writer){
if (remaining == 0){
BlockSwitchCommand nextCommand = queue.Dequeue();
BlockSwitchCommand.Serialize(writer, nextCommand, context!);
UpdateState(nextCommand);
}
protected override BlockSwitchCommand GetNextCommand(){
var nextCommand = queue[index++];
BlockSwitchCommand.Serialize(writer, nextCommand, context!);
return nextCommand;
}
}
/// <summary>
/// Tracks the current block type, pulling commands from a list whenever the current block ends.
/// </summary>
public sealed class Simulating : BlockSwitchTracker{
private readonly IReadOnlyList<BlockSwitchCommand> queue;
private int index;
--remaining;
return currentID;
public Simulating(BlockTypeInfo info, IReadOnlyList<BlockSwitchCommand> queue) : base(info){
this.queue = queue;
}
/// <summary>
/// Simulates processing a block-switch command if there are no more symbols in the current block type, then decreases the amount of remaining symbols. Returns <see cref="BlockSwitchTracker.currentID"/>.
/// </summary>
public int SimulateCommand(){
if (remaining == 0){
UpdateState(queue.Dequeue());
}
--remaining;
return currentID;
protected override BlockSwitchCommand GetNextCommand(){
return queue[index++];
}
}
}

View File

@ -52,10 +52,12 @@ namespace BrotliLib.Brotli.Components.Data{
(reader, context) => {
CompressedHeader header = context.Header;
BrotliGlobalState state = context.State;
var blockTrackers = context.BlockTrackers;
// Insert&copy lengths
int icBlockID = context.NextBlockID(Category.InsertCopy);
int icBlockID = blockTrackers[Category.InsertCopy].Advance();
var icLengthCode = reader.ReadValue(header.InsertCopyTrees[icBlockID].Root, "length code");
var icLengthValues = reader.ReadValue(InsertCopyLengths.Deserialize, icLengthCode, "length values");
@ -67,7 +69,7 @@ namespace BrotliLib.Brotli.Components.Data{
Literal[] literals = insertLength == 0 ? Array.Empty<Literal>() : new Literal[insertLength];
for(int insertIndex = 0; insertIndex < insertLength; insertIndex++){
int blockID = context.NextBlockID(Category.Literal);
int blockID = blockTrackers[Category.Literal].Advance();
int contextID = state.NextLiteralContextID(header.LiteralCtxModes[blockID]);
int treeID = header.LiteralCtxMap.DetermineTreeID(blockID, contextID);
@ -96,7 +98,7 @@ namespace BrotliLib.Brotli.Components.Data{
distanceInfo = DistanceInfo.ImplicitCodeZero;
}
else{
int blockID = context.NextBlockID(Category.Distance);
int blockID = blockTrackers[Category.Distance].Advance();
int contextID = icLengthValues.DistanceContextID;
int treeID = header.DistanceCtxMap.DetermineTreeID(blockID, contextID);
@ -118,6 +120,8 @@ namespace BrotliLib.Brotli.Components.Data{
internal static readonly BitSerializer<InsertCopyCommand, CompressedData.DataContext> Serialize = (writer, obj, context) => {
CompressedHeader header = context.Header;
BrotliGlobalState state = context.State;
var blockTrackers = context.BlockTrackers;
bool endsAfterLiterals = obj.CopyDistance == DistanceInfo.EndsAfterLiterals;
bool implicitDistanceCodeZero = obj.CopyDistance == DistanceInfo.ImplicitCodeZero;
@ -125,7 +129,7 @@ namespace BrotliLib.Brotli.Components.Data{
// Insert&copy lengths
InsertCopyLengths icLengths = obj.Lengths;
int icBlockID = context.NextBlockID(Category.InsertCopy);
int icBlockID = blockTrackers[Category.InsertCopy].Advance();
var icLengthEntry = endsAfterLiterals ? header.InsertCopyTrees[icBlockID].FindShortest(icLengths, (code, lengths) => lengths.CanEncodeUsing(code))
: header.InsertCopyTrees[icBlockID].FindShortest(icLengths, implicitDistanceCodeZero, (code, lengths, dcz) => dcz == code.UseDistanceCodeZero && lengths.CanEncodeUsing(code));
@ -138,7 +142,7 @@ namespace BrotliLib.Brotli.Components.Data{
for(int insertIndex = 0; insertIndex < icLengths.InsertLength; insertIndex++){
var literal = obj.Literals[insertIndex];
int blockID = context.NextBlockID(Category.Literal);
int blockID = blockTrackers[Category.Literal].Advance();
int contextID = state.NextLiteralContextID(header.LiteralCtxModes[blockID]);
int treeID = header.LiteralCtxMap.DetermineTreeID(blockID, contextID);
@ -155,7 +159,7 @@ namespace BrotliLib.Brotli.Components.Data{
DistanceInfo distanceInfo = obj.CopyDistance;
if (distanceInfo.HasExplicitDistanceCode()){
int blockID = context.NextBlockID(Category.Distance);
int blockID = blockTrackers[Category.Distance].Advance();
int contextID = icLengths.DistanceContextID;
int treeID = header.DistanceCtxMap.DetermineTreeID(blockID, contextID);

View File

@ -168,7 +168,7 @@ namespace BrotliLib.Brotli.Encode.Build{
// Setup
var blockTypes = BlockTypes.Select(builder => builder.Build(GetTotalBlockLength(builder.Category), parameters));
var blockTrackers = blockTypes.Select(built => new BlockSwitchTracker.Writing(built.Info, new Queue<BlockSwitchCommand>(built.Commands)));
var blockTrackers = blockTypes.Select(built => new BlockSwitchTracker.Simulating(built.Info, built.Commands));
var literalFreq = FrequencyList<Literal>.Array(LiteralCtxMap.TreeCount);
var icLengthCodeFreq = FrequencyList<InsertCopyLengthCode>.Array(blockTypes[Category.InsertCopy].Info.TypeCount);
@ -199,13 +199,13 @@ namespace BrotliLib.Brotli.Encode.Build{
for(int icIndex = 0; icIndex < icCommandCount; icIndex++){
var icCommand = commands[icIndex];
int icBlockID = blockTrackers[Category.InsertCopy].SimulateCommand();
int icBlockID = blockTrackers[Category.InsertCopy].Advance();
var icFreq = icLengthCodeFreq[icBlockID];
for(int literalIndex = 0; literalIndex < icCommand.Literals.Count; literalIndex++){
var literal = icCommand.Literals[literalIndex];
int blockID = blockTrackers[Category.Literal].SimulateCommand();
int blockID = blockTrackers[Category.Literal].Advance();
int contextID = state.NextLiteralContextID(LiteralContextModes[blockID]);
int treeID = LiteralCtxMap.DetermineTreeID(blockID, contextID);
@ -238,7 +238,7 @@ namespace BrotliLib.Brotli.Encode.Build{
icLengthCode = icLengthValues.MakeCode(ImplicitDistanceCodeZero.ForceEnabled);
}
else{
int blockID = blockTrackers[Category.Distance].SimulateCommand();
int blockID = blockTrackers[Category.Distance].Advance();
int contextID = icLengthValues.DistanceContextID;
int treeID = DistanceCtxMap.DetermineTreeID(blockID, contextID);