mirror of
https://github.com/chylex/Brotli-Builder.git
synced 2025-01-09 14:42:50 +01:00
127 lines
4.8 KiB
C#
127 lines
4.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using BrotliLib.Brotli.Components;
|
|
using BrotliLib.Brotli.Dictionary;
|
|
using BrotliLib.Brotli.Encode;
|
|
using BrotliLib.Brotli.Markers;
|
|
using BrotliLib.Brotli.State;
|
|
using BrotliLib.Brotli.State.Output;
|
|
using BrotliLib.IO;
|
|
|
|
namespace BrotliLib.Brotli{
|
|
/// <summary>
|
|
/// Represents the object structure of a Brotli-compressed file.
|
|
/// https://tools.ietf.org/html/rfc7932#section-2
|
|
/// </summary>
|
|
public sealed class BrotliFileStructure{
|
|
public static BrotliFileStructure NewEmpty(){
|
|
BrotliFileStructure bfs = new BrotliFileStructure(new BrotliFileParameters());
|
|
bfs.MetaBlocks.Add(new MetaBlock.LastEmpty());
|
|
return bfs;
|
|
}
|
|
|
|
public static BrotliFileStructure FromBytes(byte[] bytes){
|
|
return Serializer.FromBits(CreateReader(new BitStream(bytes), enableMarkers: false), new FileContext(BrotliDefaultDictionary.Embedded, windowSize => new BrotliOutputWindowed(windowSize)));
|
|
}
|
|
|
|
public static BrotliFileStructure FromEncoder(WindowSize windowSize, IBrotliEncoder encoder, byte[] bytes){
|
|
BrotliFileStructure bfs = new BrotliFileStructure(windowSize);
|
|
|
|
foreach(MetaBlock metaBlock in encoder.GenerateMetaBlocks(windowSize, bytes)){
|
|
bfs.MetaBlocks.Add(metaBlock);
|
|
}
|
|
|
|
return bfs;
|
|
}
|
|
|
|
// Data
|
|
|
|
public BrotliFileParameters Parameters { get; set; }
|
|
public IList<MetaBlock> MetaBlocks { get; }
|
|
|
|
private BrotliFileStructure(BrotliFileParameters parameters){
|
|
this.Parameters = parameters;
|
|
this.MetaBlocks = new List<MetaBlock>();
|
|
}
|
|
|
|
public void Fixup(){
|
|
for(int index = 0, last = MetaBlocks.Count - 1; index <= last; index++){
|
|
MetaBlocks[index].IsLast = index == last;
|
|
}
|
|
}
|
|
|
|
public BitStream Serialize(){
|
|
BitStream stream = new BitStream();
|
|
Serializer.ToBits(stream.GetWriter(), this, new FileContext(Parameters.Dictionary, new BrotliOutputWindowed(Parameters.WindowSize)));
|
|
return stream;
|
|
}
|
|
|
|
public BrotliOutputStored GetDecompressionState(BitStream bitStream, bool enableMarkers){
|
|
var outputState = new BrotliOutputStored();
|
|
|
|
MarkedBitReader reader = CreateReader(bitStream, enableMarkers);
|
|
|
|
Serializer.FromBits(reader, new FileContext(Parameters.Dictionary, outputState));
|
|
outputState.BitMarkerRoot = reader.MarkerRoot;
|
|
return outputState;
|
|
}
|
|
|
|
public override string ToString(){
|
|
return Serialize().ToString();
|
|
}
|
|
|
|
// Serialization
|
|
|
|
private class FileContext{
|
|
public BrotliDictionary Dictionary { get; }
|
|
public Func<WindowSize, IBrotliOutputState> OutputState { get; }
|
|
|
|
public FileContext(BrotliDictionary dictionary, Func<WindowSize, IBrotliOutputState> outputState){
|
|
Dictionary = dictionary;
|
|
OutputState = outputState;
|
|
}
|
|
|
|
public FileContext(BrotliDictionary dictionary, IBrotliOutputState outputState) : this(dictionary, _ => outputState){}
|
|
}
|
|
|
|
private static MarkedBitReader CreateReader(BitStream bitStream, bool enableMarkers){
|
|
return enableMarkers ? new MarkedBitReader(bitStream.GetReader()) : new MarkedBitReader.Dummy(bitStream.GetReader());
|
|
}
|
|
|
|
private static readonly IBitSerializer<BrotliFileStructure, FileContext> Serializer = new BitSerializer<BrotliFileStructure, FileContext>(
|
|
fromBits: (reader, context) => {
|
|
WindowSize windowSize = WindowSize.Serializer.FromBits(reader, NoContext.Value);
|
|
|
|
var parameters = new BrotliFileParameters(windowSize, context.Dictionary);
|
|
var structure = new BrotliFileStructure(parameters);
|
|
|
|
var state = new BrotliGlobalState(parameters, context.OutputState(windowSize));
|
|
|
|
while(true){
|
|
MetaBlock metaBlock = MetaBlock.Serializer.FromBits(reader, state);
|
|
structure.MetaBlocks.Add(metaBlock);
|
|
|
|
if (metaBlock.IsLast){
|
|
break;
|
|
}
|
|
}
|
|
|
|
return structure;
|
|
},
|
|
|
|
toBits: (writer, obj, context) => {
|
|
var parameters = obj.Parameters;
|
|
var windowSize = parameters.WindowSize;
|
|
|
|
WindowSize.Serializer.ToBits(writer, windowSize, NoContext.Value);
|
|
|
|
var state = new BrotliGlobalState(parameters, context.OutputState(windowSize));
|
|
|
|
foreach(MetaBlock metaBlock in obj.MetaBlocks){
|
|
MetaBlock.Serializer.ToBits(writer, metaBlock, state);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|