From 8fc071d4d8ba576133f24cb931f4ee1034ed5b1a Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 11 Dec 2024 09:45:03 -0500 Subject: [PATCH] Better modernization of Blast --- SabreTools.Compression/Blast/Decompressor.cs | 26 ++--- SabreTools.Compression/Blast/Huffman.cs | 11 +- SabreTools.Compression/Blast/State.cs | 106 ++++++++++++------- 3 files changed, 77 insertions(+), 66 deletions(-) diff --git a/SabreTools.Compression/Blast/Decompressor.cs b/SabreTools.Compression/Blast/Decompressor.cs index 721710d..1fb8d44 100644 --- a/SabreTools.Compression/Blast/Decompressor.cs +++ b/SabreTools.Compression/Blast/Decompressor.cs @@ -146,7 +146,7 @@ namespace SabreTools.Compression.Blast int err; try { - err = Decomp(state); + err = Decompress(state); } catch (IndexOutOfRangeException) { @@ -198,7 +198,7 @@ namespace SabreTools.Compression.Blast /// ignoring whether the length is greater than the distance or not implements /// this correctly. /// - private int Decomp(State state) + private int Decompress(State state) { int symbol; // decoded symbol, extra bits for distance int len; // length for copy @@ -207,29 +207,29 @@ namespace SabreTools.Compression.Blast int from, to; // copy pointers // Read header - int lit = state.Bits(8); // true if literals are coded + int lit = state.ReadBits(8); // true if literals are coded if (lit > 1) return -1; - int dict = state.Bits(8); // log2(dictionary size) - 6 + int dict = state.ReadBits(8); // log2(dictionary size) - 6 if (dict < 4 || dict > 6) return -2; // Decode literals and length/distance pairs while (true) { - if (state.Bits(1) != 0) + if (state.ReadBits(1) != 0) { // Get length symbol = lencode.Decode(state); - len = baseLength[symbol] + state.Bits(extra[symbol]); + len = baseLength[symbol] + state.ReadBits(extra[symbol]); if (len == 519) break; // end code // Get distance symbol = len == 2 ? 2 : dict; dist = (uint)(distcode.Decode(state) << symbol); - dist += (uint)state.Bits(symbol); + dist += (uint)state.ReadBits(symbol); dist++; if (state.First && dist > state.Next) return -3; //distance too far back @@ -237,7 +237,7 @@ namespace SabreTools.Compression.Blast // Copy length bytes from distance bytes back do { - to = (int)(state.OutputPtr + state.Next); + to = (int)state.Next; from = (int)(to - dist); copy = MAXWIN; if (state.Next < dist) @@ -252,11 +252,7 @@ namespace SabreTools.Compression.Blast len -= copy; state.Next += (uint)copy; - do - { - state.Output[to++] = state.Output[from++]; - } - while (--copy != 0); + state.CopyOutputBytes(to, from, copy); if (state.Next == MAXWIN) { @@ -272,8 +268,8 @@ namespace SabreTools.Compression.Blast else { // Get literal and write it - symbol = lit != 0 ? litcode.Decode(state) : state.Bits(8); - state.Output[state.Next++] = (byte)symbol; + symbol = lit != 0 ? litcode.Decode(state) : state.ReadBits(8); + state.WriteToOutput((byte)symbol); if (state.Next == MAXWIN) { if (!state.ProcessOutput()) diff --git a/SabreTools.Compression/Blast/Huffman.cs b/SabreTools.Compression/Blast/Huffman.cs index 70dd3a6..7629efa 100644 --- a/SabreTools.Compression/Blast/Huffman.cs +++ b/SabreTools.Compression/Blast/Huffman.cs @@ -1,4 +1,3 @@ -using System; using static SabreTools.Compression.Blast.Constants; namespace SabreTools.Compression.Blast @@ -187,15 +186,7 @@ namespace SabreTools.Compression.Blast if (left == 0) break; - if (state.Left == 0) - { - state.Left = state.ProcessInput(); - if (state.Left == 0) - throw new IndexOutOfRangeException(); - } - - bitbuf = state.Input[state.InputPtr++]; - state.Left--; + bitbuf = state.ReadNextByte(); if (left > 8) left = 8; } diff --git a/SabreTools.Compression/Blast/State.cs b/SabreTools.Compression/Blast/State.cs index 73641e3..b533e97 100644 --- a/SabreTools.Compression/Blast/State.cs +++ b/SabreTools.Compression/Blast/State.cs @@ -14,22 +14,22 @@ namespace SabreTools.Compression.Blast /// /// Opaque information passed to InputFunction() /// - public Stream Source { get; set; } - + private readonly Stream _source; + /// /// Next input location /// - public byte[] Input { get; set; } + private readonly byte[] _input = new byte[MAXWIN]; /// /// Pointer to the next input location /// - public int InputPtr { get; set; } + private int _inputPtr; /// /// Available input at in /// - public uint Left { get; set; } + private uint _available; /// /// Bit buffer @@ -48,7 +48,7 @@ namespace SabreTools.Compression.Blast /// /// Opaque information passed to OutputFunction() /// - public Stream Dest { get; set; } + private readonly Stream _dest; /// /// Index of next write location in out[] @@ -63,12 +63,7 @@ namespace SabreTools.Compression.Blast /// /// Output buffer and sliding window /// - public readonly byte[] Output = new byte[MAXWIN]; - - /// - /// Pointer to the next output location - /// - public int OutputPtr { get; set; } + private readonly byte[] _output = new byte[MAXWIN]; #endregion @@ -77,18 +72,29 @@ namespace SabreTools.Compression.Blast /// public State(Stream source, Stream dest) { - Source = source; - Input = []; - InputPtr = 0; - Left = 0; + _source = source; + _inputPtr = 0; + _available = 0; BitBuf = 0; BitCnt = 0; - Dest = dest; + _dest = dest; Next = 0; First = true; } + /// + /// Copy bytes in the output buffer between locations + /// + public void CopyOutputBytes(int to, int from, int len) + { + do + { + _output[to++] = _output[from++]; + } + while (--len > 0); + } + /// /// Return need bits from the input stream. This always leaves less than /// eight bits in the buffer. bits() works properly for need == 0. @@ -100,22 +106,16 @@ namespace SabreTools.Compression.Blast /// buffer, using shift right, and new bytes are appended to the top of the /// bit buffer, using shift left. /// - public int Bits(int need) + public int ReadBits(int need) { // Load at least need bits into val int val = BitBuf; while (BitCnt < need) { - if (Left == 0) - { - Left = ProcessInput(); - if (Left == 0) - throw new IndexOutOfRangeException(); - } - // Load eight bits - val |= Input[InputPtr++] << BitCnt; - Left--; + EnsureAvailable(); + val |= _input[_inputPtr++] << BitCnt; + _available--; BitCnt += 8; } @@ -127,16 +127,6 @@ namespace SabreTools.Compression.Blast return val & ((1 << need) - 1); } - /// - /// Process input for the current state - /// - /// Amount of data in Input - public uint ProcessInput() - { - int read = Source.Read(Input, 0, 4096); - return (uint)read; - } - /// /// Process output for the current state /// @@ -145,10 +135,10 @@ namespace SabreTools.Compression.Blast { try { - byte[] next = new byte[Next]; - Array.Copy(Output, next, next.Length); - Dest.Write(next); - Dest.Flush(); + _dest.Write(_output, 0, (int)Next); + _dest.Flush(); + + Next = 0; return true; } catch @@ -156,5 +146,39 @@ namespace SabreTools.Compression.Blast return false; } } + + /// + /// Read the next byte from the input buffer + /// + public byte ReadNextByte() + { + EnsureAvailable(); + return _input[_inputPtr++]; + } + + /// + /// Write a byte value to the output buffer + /// + public void WriteToOutput(byte value) + => _output[Next++] = value; + + /// + /// Ensure there are bytes available, if possible + /// + /// + private void EnsureAvailable() + { + // If there are bytes + if (_inputPtr < _available) + return; + + // Read the next block + _available = (uint)_source.Read(_input, 0, MAXWIN); + if (_available == 0) + throw new IndexOutOfRangeException(); + + // Reset the pointer + _inputPtr = 0; + } } } \ No newline at end of file