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/Header/DataLength.cs

101 lines
3.7 KiB
C#

using System;
using BrotliLib.Markers.Serialization;
using BrotliLib.Numbers;
using BrotliLib.Serialization;
namespace BrotliLib.Brotli.Components.Header{
/// <summary>
/// Describes size of uncompressed data stored in a meta-block.
/// https://tools.ietf.org/html/rfc7932#section-9.2
/// </summary>
public sealed class DataLength{
private const int MinNibbles = 4;
private const int MaxNibbles = 6;
public const int MinUncompressedBytes = 0;
public const int MaxUncompressedBytes = 1 << (4 * MaxNibbles);
public static readonly IntRange BytesRange = new IntRange(MinUncompressedBytes, MaxUncompressedBytes);
public static readonly DataLength Empty = new DataLength(0);
// Data
public int ChunkNibbles{
get{
if (UncompressedBytes == 0){
return 0;
}
for(int nibbles = MinNibbles; nibbles <= MaxNibbles; nibbles++){
int maxValue = 1 << (4 * nibbles);
if (UncompressedBytes <= maxValue){
return nibbles;
}
}
throw new InvalidOperationException("The amount of bytes (" + UncompressedBytes + ") cannot be expressed with at most " + MaxNibbles + " nibbles.");
}
}
public int UncompressedBytes { get; }
public DataLength(int uncompressedBytes){
if (!BytesRange.Contains(uncompressedBytes)){
throw new ArgumentOutOfRangeException(nameof(uncompressedBytes), uncompressedBytes, "The amount of uncompressed bytes must be in the range " + BytesRange + ".");
}
this.UncompressedBytes = uncompressedBytes;
}
// Object
public override bool Equals(object obj){
return obj is DataLength length &&
ChunkNibbles == length.ChunkNibbles &&
UncompressedBytes == length.UncompressedBytes;
}
public override int GetHashCode(){
return HashCode.Combine(ChunkNibbles, UncompressedBytes);
}
public override string ToString(){
return "ChunkNibbles = " + ChunkNibbles + ", UncompressedBytes = " + UncompressedBytes;
}
// Serialization
public static readonly BitDeserializer<DataLength, NoContext> Deserialize = MarkedBitDeserializer.Title<DataLength, NoContext>(
"Data Length",
(reader, context) => {
int chunkNibbles = reader.NextChunk(2, "MNIBBLES", value => value switch{
0b00 => 4,
0b01 => 5,
0b10 => 6,
0b11 => 0,
_ => throw new InvalidOperationException("Reading two bits somehow returned a value outside [0; 3]."),
});
int uncompressedBytes = (chunkNibbles == 0) ? 0 : reader.NextChunk(4 * chunkNibbles, "MLEN", value => 1 + value);
return new DataLength(uncompressedBytes);
}
);
public static readonly BitSerializer<DataLength, NoContext> Serialize = (writer, obj, context) => {
switch(obj.ChunkNibbles){
case 4: writer.WriteChunk(2, 0b00); break;
case 5: writer.WriteChunk(2, 0b01); break;
case 6: writer.WriteChunk(2, 0b10); break;
case 0: writer.WriteChunk(2, 0b11); break;
default: throw new InvalidOperationException("Data length object has an invalid amount of nibbles: " + obj.ChunkNibbles);
}
writer.WriteChunk(4 * obj.ChunkNibbles, obj.UncompressedBytes - 1);
};
}
}