mirror of
https://github.com/chylex/Brotli-Builder.git
synced 2024-11-25 07:42:56 +01:00
123 lines
5.1 KiB
C#
123 lines
5.1 KiB
C#
using System;
|
|
using System.Linq;
|
|
using BrotliLib.Brotli.Components.Header;
|
|
using BrotliLib.Brotli.Utils;
|
|
using BrotliLib.Numbers;
|
|
using InsertCopyTree = BrotliLib.Brotli.Components.Header.HuffmanTree<BrotliLib.Brotli.Components.Data.InsertCopyLengthCode>;
|
|
|
|
namespace BrotliLib.Brotli.Components.Data{
|
|
/// <summary>
|
|
/// Describes a <see cref="HuffmanTree{T}"/> symbol used to calculate lengths of an insert&copy code.
|
|
/// https://tools.ietf.org/html/rfc7932#section-5
|
|
/// </summary>
|
|
public sealed class InsertCopyLengthCode : IComparable<InsertCopyLengthCode>{
|
|
public static readonly AlphabetSize AlphabetSize = new AlphabetSize(704);
|
|
public static readonly InsertCopyTree.Context TreeContext = new InsertCopyTree.Context(AlphabetSize, value => new InsertCopyLengthCode(value), symbol => symbol.CompactedCode);
|
|
|
|
// Cell offsets
|
|
|
|
private static readonly int[] InsertCellOffsets = {
|
|
0, 0, 0, 0, 8, 8, 0, 16, 8, 16, 16
|
|
};
|
|
|
|
private static readonly int[] CopyCellOffsets = {
|
|
0, 8, 0, 8, 0, 8, 16, 0, 16, 8, 16
|
|
};
|
|
|
|
private static readonly (IntRange i, IntRange c)[] PairedCellOffsets = InsertCellOffsets.Zip(CopyCellOffsets, (i, c) => (new IntRange(i, i + 7), new IntRange(c, c + 7))).ToArray();
|
|
|
|
private static int DetermineCellIndex(int insertCode, int copyCode, bool useDistanceCodeZero){
|
|
int startCellIndex = useDistanceCodeZero ? 0 : 2;
|
|
|
|
for(int index = startCellIndex; index < PairedCellOffsets.Length; index++){
|
|
var (i, c) = PairedCellOffsets[index];
|
|
|
|
if (i.Contains(insertCode) && c.Contains(copyCode)){
|
|
return index;
|
|
}
|
|
}
|
|
|
|
throw new ArgumentException("Could not determine cell index of insert© length code.");
|
|
}
|
|
|
|
// Data
|
|
|
|
/// <summary>
|
|
/// Combined <see cref="InsertCode"/> and <see cref="CopyCode"/> values into a single value from the insert&copy alphabet.
|
|
/// </summary>
|
|
public int CompactedCode { get; }
|
|
|
|
/// <summary>
|
|
/// Code used to determine the <see cref="InsertCopyLengths.InsertLength"/> value.
|
|
/// </summary>
|
|
public int InsertCode { get; }
|
|
|
|
/// <summary>
|
|
/// Code used to determine the <see cref="InsertCopyLengths.CopyLength"/> value.
|
|
/// </summary>
|
|
public int CopyCode { get; }
|
|
|
|
/// <summary>
|
|
/// Whether to skip reading the <see cref="DistanceCode"/>, and use distance code 0 instead.
|
|
/// </summary>
|
|
public bool UseDistanceCodeZero { get; }
|
|
|
|
/// <summary>
|
|
/// Initializes the code with a value from the insert&copy alphabet.
|
|
/// </summary>
|
|
public InsertCopyLengthCode(int compactedCode){
|
|
if (compactedCode < 0 || compactedCode >= AlphabetSize.SymbolCount){
|
|
throw new ArgumentOutOfRangeException(nameof(compactedCode), "Compacted insert© length code must be in the range [0; " + AlphabetSize.SymbolCount + ").");
|
|
}
|
|
|
|
int cell = compactedCode / 64;
|
|
|
|
this.CompactedCode = compactedCode;
|
|
this.InsertCode = InsertCellOffsets[cell] + ((compactedCode >> 3) & 0b111);
|
|
this.CopyCode = CopyCellOffsets[cell] + (compactedCode & 0b111);
|
|
this.UseDistanceCodeZero = cell < 2;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the code with the concrete insert and copy codes, and the flag which determines whether to use an implied distance code zero.
|
|
/// </summary>
|
|
public InsertCopyLengthCode(int insertCode, int copyCode, ImplicitDistanceCodeZero implicitDCZ){
|
|
if (insertCode < 0 || insertCode > 23){
|
|
throw new ArgumentOutOfRangeException(nameof(insertCode), "Insert code must be in the range [0; 23].");
|
|
}
|
|
|
|
if (copyCode < 0 || copyCode > 23){
|
|
throw new ArgumentOutOfRangeException(nameof(copyCode), "Copy code must be in the range [0; 23].");
|
|
}
|
|
|
|
bool useDistanceCodeZero = implicitDCZ.Decide(insertCode, copyCode);
|
|
|
|
int cell = DetermineCellIndex(insertCode, copyCode, useDistanceCodeZero);
|
|
|
|
this.CompactedCode = (64 * cell) + ((insertCode & 0b111) << 3) | (copyCode & 0b111);
|
|
this.InsertCode = insertCode;
|
|
this.CopyCode = copyCode;
|
|
this.UseDistanceCodeZero = useDistanceCodeZero;
|
|
}
|
|
|
|
public int CompareTo(InsertCopyLengthCode other){
|
|
return CompactedCode.CompareTo(other.CompactedCode);
|
|
}
|
|
|
|
// Object
|
|
|
|
public override bool Equals(object obj){
|
|
return obj is InsertCopyLengthCode code &&
|
|
CompactedCode == code.CompactedCode;
|
|
}
|
|
|
|
public override int GetHashCode(){
|
|
return HashCode.Combine(CompactedCode);
|
|
}
|
|
|
|
public override string ToString(){
|
|
return "Code = " + CompactedCode + " (InsertCode = " + InsertCode + ", CopyCode = " + CopyCode + (UseDistanceCodeZero ? ", DistanceCodeZero" : "") + ")";
|
|
}
|
|
}
|
|
}
|