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:
parent
777f6de11f
commit
4cdf3825d2
BrotliLib/Brotli
Components
Encode/Build
@ -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]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,10 +52,12 @@ namespace BrotliLib.Brotli.Components.Data{
|
||||
(reader, context) => {
|
||||
CompressedHeader header = context.Header;
|
||||
BrotliGlobalState state = context.State;
|
||||
|
||||
var blockTrackers = context.BlockTrackers;
|
||||
|
||||
// Insert© 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© 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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user