この記事の目的
C#でBrotliEncoder/BrotliDecoderを使った際に苦戦したので、試行錯誤の結果を自分以外の人に共有したい。
前提条件
- .Net 6を想定。
- エンコードとデコードのInputは
ReadOnlySequence<byte>、OutputはIBufferWriter<byte>とする。
サンプルコード
Compress
public static void Compress(ReadOnlySequence<byte> sequence, IBufferWriter<byte> writer)
{
using var encoder = new BrotliEncoder(11, 24);
var reader = new SequenceReader<byte>(sequence);
for (; ; )
{
var status = encoder.Compress(reader.UnreadSpan, writer.GetSpan(), out var consumed, out var written, false);
if (status == OperationStatus.InvalidData) throw new Exception("invalid data");
reader.Advance(consumed);
writer.Advance(written);
if (status == OperationStatus.Done) break;
}
for (; ; )
{
var status = encoder.Compress(ReadOnlySpan<byte>.Empty, writer.GetSpan(), out _, out var written, true);
if (status == OperationStatus.InvalidData) throw new Exception("invalid data");
writer.Advance(written);
if (written == 0) break;
}
}
訂正 (2022/10/08)
neueccさんにご指摘いただき、Compressが正しく処理できていないことが分かりました![]()
statusがOperationStatus.Doneなら圧縮完了だと理解していたのですが、正しくは
The entire input buffer has been processed and the operation is complete.
とドキュメントにも記載されている通り、inputの処理が完了したのみで、flush処理を別途行う必要がありました...
neueccさんありがとうございました![]()
Decompress
public static void Decompress(ReadOnlySequence<byte> sequence, IBufferWriter<byte> writer)
{
var reader = new SequenceReader<byte>(sequence);
using var decoder = new BrotliDecoder();
for (; ; )
{
var status = decoder.Decompress(reader.UnreadSpan, writer.GetSpan(), out var consumed, out var written);
if (status == OperationStatus.InvalidData) throw new Exception("invalid data");
reader.Advance(consumed);
writer.Advance(written);
if (written == 0) break;
}
}
ハマったポイント
-
OperationStatus.DestinationTooSmallはエラーというよりも、destinationに最大まで書き込んだというお知らせなので、新しいdestinationを指定すればよい。 -
BrotliEncoder.Compressで-> isFinalBlock: trueで最後に処理すればOKでしたisFinalBlockにfalseを指定した場合、Decompress時にデータの末尾になってもOperationStatus.DoneではなくOperationStatus.NeedMoreDataが常に返ってくる。