mirror of
https://github.com/chylex/Brotli-Builder.git
synced 2024-11-24 22:42:50 +01:00
133 lines
5.1 KiB
C#
133 lines
5.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using BrotliImpl.Utils;
|
|
using BrotliLib.Brotli.Components;
|
|
using BrotliLib.Brotli.Components.Data;
|
|
using BrotliLib.Brotli.Components.Header;
|
|
using BrotliLib.Brotli.Encode;
|
|
using BrotliLib.Brotli.Parameters;
|
|
|
|
namespace BrotliImpl.Encoders{
|
|
/// <summary>
|
|
/// Encodes bytes into a series of compressed meta-blocks. For each byte, it attempts to find the nearest and longest copy within the sliding window, or dictionary word.
|
|
/// </summary>
|
|
public abstract class EncodeGreedySearch : IBrotliEncoder{
|
|
private protected abstract Copy? FindCopy(BrotliFileParameters parameters, in ArraySegment<byte> bytes, int start, int maxLength);
|
|
|
|
// Implementations
|
|
|
|
public sealed class OnlyBackReferences : EncodeGreedySearch{
|
|
private readonly int minLength;
|
|
|
|
public OnlyBackReferences(int minLength){
|
|
this.minLength = Math.Max(minLength, InsertCopyLengths.MinCopyLength);
|
|
}
|
|
|
|
private protected override Copy? FindCopy(BrotliFileParameters parameters, in ArraySegment<byte> bytes, int start, int maxLength){
|
|
int length = bytes.Count;
|
|
|
|
if (start < InsertCopyLengths.MinCopyLength || start >= length - InsertCopyLengths.MinCopyLength || maxLength < InsertCopyLengths.MinCopyLength){
|
|
return null;
|
|
}
|
|
|
|
maxLength = Math.Min(maxLength, InsertCopyLengths.MaxCopyLength);
|
|
int maxDistance = Math.Min(start, parameters.WindowSize.Bytes);
|
|
|
|
for(int distance = 1; distance <= maxDistance; distance++){
|
|
int match = Match.DetermineLength(bytes, start, start - distance, Math.Min(maxLength, length - start));
|
|
|
|
if (match >= minLength){
|
|
return new Copy.BackReference(match, distance);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public sealed class OnlyDictionary : EncodeGreedySearch{
|
|
private readonly int minLength;
|
|
|
|
public OnlyDictionary(int minLength){
|
|
this.minLength = minLength;
|
|
}
|
|
|
|
private protected override Copy? FindCopy(BrotliFileParameters parameters, in ArraySegment<byte> bytes, int start, int maxLength){
|
|
var entries = parameters.Dictionary.Index.Find(bytes.Slice(start), minLength, maxLength);
|
|
|
|
if (entries.Count == 0){
|
|
return null;
|
|
}
|
|
|
|
var bestEntry = entries[0];
|
|
int bestOutputLength = bestEntry.OutputLength;
|
|
int bestPacked = bestEntry.Packed;
|
|
|
|
for(int index = 1; index < entries.Count; index++){
|
|
var entry = entries[index];
|
|
|
|
if (entry.OutputLength > bestOutputLength || (entry.OutputLength == bestOutputLength && entry.Packed < bestPacked)){
|
|
bestEntry = entry;
|
|
}
|
|
}
|
|
|
|
return new Copy.Dictionary(bestEntry);
|
|
}
|
|
}
|
|
|
|
public sealed class Mixed : EncodeGreedySearch{
|
|
private readonly EncodeGreedySearch findBackReferences;
|
|
private readonly EncodeGreedySearch findDictionary;
|
|
|
|
public Mixed(int minCopyLength, int minDictionaryLength){
|
|
this.findBackReferences = new OnlyBackReferences(minCopyLength);
|
|
this.findDictionary = new OnlyDictionary(minDictionaryLength);
|
|
}
|
|
|
|
private protected override Copy? FindCopy(BrotliFileParameters parameters, in ArraySegment<byte> bytes, int start, int maxLength){
|
|
Copy? found1 = findBackReferences.FindCopy(parameters, bytes, start, maxLength);
|
|
Copy? found2 = findDictionary.FindCopy(parameters, bytes, start, maxLength);
|
|
|
|
return (found1?.OutputLength ?? 0) >= (found2?.OutputLength ?? 0) ? found1 : found2;
|
|
}
|
|
}
|
|
|
|
// Generation
|
|
|
|
public (MetaBlock, BrotliEncodeInfo) Encode(BrotliEncodeInfo info){
|
|
var bytes = info.Bytes;
|
|
int length = bytes.Count;
|
|
|
|
var builder = info.NewBuilder();
|
|
var nextLiteralBatch = new List<Literal>();
|
|
|
|
for(int index = 0; index < length;){
|
|
var copy = FindCopy(info.FileParameters, in bytes, index, DataLength.MaxUncompressedBytes - nextLiteralBatch.Count);
|
|
int mbSize;
|
|
|
|
if (copy == null){
|
|
nextLiteralBatch.Add(new Literal(bytes[index]));
|
|
index++;
|
|
|
|
mbSize = nextLiteralBatch.Count;
|
|
}
|
|
else{
|
|
index += copy.AddCommand(builder, nextLiteralBatch);
|
|
nextLiteralBatch.Clear();
|
|
mbSize = builder.OutputSize;
|
|
}
|
|
|
|
if (mbSize == DataLength.MaxUncompressedBytes){
|
|
return builder.Build(info);
|
|
}
|
|
}
|
|
|
|
if (nextLiteralBatch.Count > 0){
|
|
builder.AddInsert(nextLiteralBatch);
|
|
}
|
|
|
|
return builder.Build(info);
|
|
}
|
|
}
|
|
}
|