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/Compressed/CompressedData.cs

149 lines
6.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BrotliLib.Brotli.Components.Data;
using BrotliLib.Brotli.Components.Header;
using BrotliLib.Brotli.Output;
using BrotliLib.Brotli.Utils;
using BrotliLib.Collections;
using BrotliLib.Markers.Serialization;
using BrotliLib.Markers.Serialization.Reader;
using BrotliLib.Markers.Types;
using BrotliLib.Serialization;
using BrotliLib.Serialization.Writer;
using BlockSwitchCommandMap = BrotliLib.Brotli.Utils.CategoryMap<System.Collections.Generic.IReadOnlyList<BrotliLib.Brotli.Components.Data.BlockSwitchCommand>>;
using BlockSwitchCommandMutableMap = BrotliLib.Brotli.Utils.CategoryMap<System.Collections.Generic.IList<BrotliLib.Brotli.Components.Data.BlockSwitchCommand>>;
namespace BrotliLib.Brotli.Components.Compressed{
public sealed class CompressedData{
public IReadOnlyList<InsertCopyCommand> InsertCopyCommands { get; }
public BlockSwitchCommandMap BlockSwitchCommands { get; }
public CompressedData(IList<InsertCopyCommand> insertCopyCommands, BlockSwitchCommandMap blockSwitchCommands){
this.InsertCopyCommands = insertCopyCommands.ToArray();
this.BlockSwitchCommands = blockSwitchCommands;
}
public CompressedData(IList<InsertCopyCommand> insertCopyCommands, BlockSwitchCommandMutableMap blockSwitchCommands) :
this(insertCopyCommands, blockSwitchCommands.Select<IReadOnlyList<BlockSwitchCommand>>(list => list.ToArray())){}
// Object
public override bool Equals(object obj){
return obj is CompressedData contents &&
CollectionHelper.Equal(InsertCopyCommands, contents.InsertCopyCommands) &&
Categories.LID.All(category => CollectionHelper.Equal(BlockSwitchCommands[category], contents.BlockSwitchCommands[category]));
}
public override int GetHashCode(){
return HashCode.Combine(CollectionHelper.HashCode(InsertCopyCommands), BlockSwitchCommands.Select(CollectionHelper.HashCode).GetHashCode());
}
// Context
public class Context{
public CompressedHeader Header { get; }
public DataLength DataLength { get; }
public BrotliGlobalState State { get; }
public Context(CompressedHeader header, DataLength dataLength, BrotliGlobalState state){
this.Header = header;
this.DataLength = dataLength;
this.State = state;
}
}
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 void WriteLiteral(in Literal literal){
State.OutputLiteral(literal);
++bytesWritten;
}
public void WriteCopy(int length, DistanceInfo distance){
bytesWritten += State.OutputCopy(length, distance).BytesWritten;
}
public void WriteCopyWithMarker(IMarkedBitReader reader, int length, DistanceInfo distance){
if (reader is MarkedBitReaderDummy){
WriteCopy(length, distance);
}
else{
reader.MarkStart();
var written = new BrotliOutputStored();
written.Write((byte)'"');
State.AddOutputCallback(written);
CopyOutputInfo copy = WriteCopyTracked(length, distance);
State.RemoveOutputCallback(written);
written.Write((byte)'"');
reader.MarkEnd(new ValueMarker(copy.IsBackReference ? "backreference" : "dictionary", Encoding.UTF8.GetString(written.AsBytes)));
}
}
private CopyOutputInfo WriteCopyTracked(int length, DistanceInfo distance){
var info = State.OutputCopy(length, distance);
bytesWritten += info.BytesWritten;
return info;
}
}
private class ReaderDataContext : DataContext{
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.readingTrackers = Header.BlockTypes.Select(info => new BlockSwitchTracker.Reading(info, reader));
this.BlockTrackers = readingTrackers.Select<BlockSwitchTracker>(tracker => tracker);
}
}
private class WriterDataContext : DataContext{
public override CategoryMap<BlockSwitchTracker> BlockTrackers { get; }
public WriterDataContext(Context wrapped, BlockSwitchCommandMap blockSwitchCommands, IBitWriter writer) : base(wrapped){
this.BlockTrackers = Header.BlockTypes.Select<BlockSwitchTracker>(info => new BlockSwitchTracker.Writing(info, writer, blockSwitchCommands[info.Category]));
}
}
// Serialization
public static readonly BitDeserializer<CompressedData, Context> Deserialize = MarkedBitDeserializer.Wrap<CompressedData, Context>(
(reader, context) => {
ReaderDataContext dataContext = new ReaderDataContext(context, reader);
List<InsertCopyCommand> icCommands = new List<InsertCopyCommand>();
reader.MarkStart();
do{
icCommands.Add(InsertCopyCommand.Deserialize(reader, dataContext));
}while(dataContext.NeedsMoreData);
reader.MarkEndTitle("Command List");
return new CompressedData(icCommands, dataContext.BlockSwitchCommands);
}
);
public static readonly BitSerializer<CompressedData, Context> Serialize = (writer, obj, context) => {
DataContext dataContext = new WriterDataContext(context, obj.BlockSwitchCommands, writer);
foreach(InsertCopyCommand icCommand in obj.InsertCopyCommands){
InsertCopyCommand.Serialize(writer, icCommand, dataContext);
}
};
}
}