1
0
mirror of https://github.com/chylex/Brotli-Builder.git synced 2024-11-24 22:42:50 +01:00
Brotli-Builder/BrotliLib/Brotli/Components/Data/InsertCopyCommand.cs

183 lines
7.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using BrotliLib.Brotli.Components.Compressed;
using BrotliLib.Brotli.Utils;
using BrotliLib.Collections;
using BrotliLib.Markers;
using BrotliLib.Markers.Serialization;
using BrotliLib.Serialization;
namespace BrotliLib.Brotli.Components.Data{
public sealed class InsertCopyCommand{
public IReadOnlyList<Literal> Literals { get; }
public int CopyLength { get; }
public DistanceInfo CopyDistance { get; }
public InsertCopyLengths Lengths => new InsertCopyLengths(Literals.Count, CopyLength);
public InsertCopyCommand(IList<Literal> literals, int copyLength = InsertCopyLengths.MinCopyLength, DistanceInfo copyDistance = DistanceInfo.EndsAfterLiterals){
InsertCopyLengths.CheckBounds(literals.Count, copyLength);
this.Literals = literals.ToArray();
this.CopyLength = copyLength;
this.CopyDistance = copyDistance;
}
public InsertCopyCommand(IList<Literal> literals, int copyLength, int copyDistance) : this(literals, copyLength, (DistanceInfo)copyDistance){}
private InsertCopyCommand(Literal[] literals, int copyLength, DistanceInfo copyDistance){
this.Literals = literals;
this.CopyLength = copyLength;
this.CopyDistance = copyDistance;
}
// Object
public override bool Equals(object obj){
return obj is InsertCopyCommand command &&
CollectionHelper.Equal(Literals, command.Literals) &&
CopyLength == command.CopyLength &&
CopyDistance == command.CopyDistance;
}
public override int GetHashCode(){
return HashCode.Combine(CollectionHelper.HashCode(Literals), CopyLength, CopyDistance);
}
public override string ToString(){
return Lengths + ", CopyDistance = " + CopyDistance;
}
// Serialization
internal static readonly BitDeserializer<InsertCopyCommand, CompressedData.DataContext> Deserialize = MarkedBitDeserializer.Title<InsertCopyCommand, CompressedData.DataContext>(
"Insert & Copy Command",
(reader, context) => {
CompressedHeader header = context.Header;
BrotliGlobalState state = context.State;
var blockTrackers = context.BlockTrackers;
// Insert&copy lengths
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");
int insertLength = icLengthValues.InsertLength;
int copyLength = icLengthValues.CopyLength;
// Literals
Literal[] literals = insertLength == 0 ? Array.Empty<Literal>() : new Literal[insertLength];
for(int insertIndex = 0; insertIndex < insertLength; insertIndex++){
int blockID = blockTrackers[Category.Literal].Advance();
int contextID = state.NextLiteralContextID(header.LiteralCtxModes[blockID]);
int treeID = header.LiteralCtxMap.DetermineTreeID(blockID, contextID);
Literal literal;
if (reader.MarkerLevel == MarkerLevel.Verbose){
literal = reader.ReadValue(header.LiteralTrees[treeID].Root, "literal");
}
else{
literal = header.LiteralTrees[treeID].Root.LookupValue(reader);
}
literals[insertIndex] = literal;
context.WriteLiteral(in literal);
}
if (!context.NeedsMoreData){
return new InsertCopyCommand(literals, copyLength, DistanceInfo.EndsAfterLiterals);
}
// Distance
DistanceInfo distanceInfo;
if (icLengthCode.UseDistanceCodeZero){
distanceInfo = DistanceInfo.ImplicitCodeZero;
}
else{
int blockID = blockTrackers[Category.Distance].Advance();
int contextID = icLengthValues.DistanceContextID;
int treeID = header.DistanceCtxMap.DetermineTreeID(blockID, contextID);
var distanceCode = reader.ReadValue(header.DistanceTrees[treeID].Root, "distance code");
distanceInfo = reader.ReadValue(DistanceCode.Deserialize, distanceCode.MakeContext(state), "distance value");
}
if (reader.MarkerLevel == MarkerLevel.Verbose){
context.WriteCopyWithMarker(reader, copyLength, distanceInfo);
}
else{
context.WriteCopy(copyLength, distanceInfo);
}
return new InsertCopyCommand(literals, copyLength, distanceInfo);
}
);
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;
// Insert&copy lengths
InsertCopyLengths icLengths = obj.Lengths;
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));
writer.WriteBits(icLengthEntry.Value);
InsertCopyLengths.Serialize(writer, icLengths, icLengthEntry.Key);
// Literals
for(int insertIndex = 0; insertIndex < icLengths.InsertLength; insertIndex++){
var literal = obj.Literals[insertIndex];
int blockID = blockTrackers[Category.Literal].Advance();
int contextID = state.NextLiteralContextID(header.LiteralCtxModes[blockID]);
int treeID = header.LiteralCtxMap.DetermineTreeID(blockID, contextID);
writer.WriteBits(header.LiteralTrees[treeID].FindPath(literal));
context.WriteLiteral(in literal);
}
// Distance
if (endsAfterLiterals){
return;
}
DistanceInfo distanceInfo = obj.CopyDistance;
if (distanceInfo.HasExplicitDistanceCode()){
int blockID = blockTrackers[Category.Distance].Advance();
int contextID = icLengths.DistanceContextID;
int treeID = header.DistanceCtxMap.DetermineTreeID(blockID, contextID);
var distanceEntry = header.DistanceTrees[treeID].FindShortest(distanceInfo, state, (code, dist, st) => dist.CanEncodeUsing(code, st), code => code.ExtraBits);
var distanceCode = distanceEntry.Key;
writer.WriteBits(distanceEntry.Value);
DistanceCode.Serialize(writer, distanceInfo, distanceCode.MakeContext(state));
}
context.WriteCopy(icLengths.CopyLength, distanceInfo);
};
}
}