123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- using System;
- using System.Diagnostics;
- namespace SharpCompress.Compressors.Deflate64
- {
- // This class can be used to read bits from an byte array quickly.
- // Normally we get bits from 'bitBuffer' field and bitsInBuffer stores
- // the number of bits available in 'BitBuffer'.
- // When we used up the bits in bitBuffer, we will try to get byte from
- // the byte array and copy the byte to appropiate position in bitBuffer.
- //
- // The byte array is not reused. We will go from 'start' to 'end'.
- // When we reach the end, most read operations will return -1,
- // which means we are running out of input.
- internal sealed class InputBuffer
- {
- private byte[] _buffer; // byte array to store input
- private int _start; // start poisition of the buffer
- private int _end; // end position of the buffer
- private uint _bitBuffer = 0; // store the bits here, we can quickly shift in this buffer
- private int _bitsInBuffer = 0; // number of bits available in bitBuffer
- /// <summary>Total bits available in the input buffer.</summary>
- public int AvailableBits => _bitsInBuffer;
- /// <summary>Total bytes available in the input buffer.</summary>
- public int AvailableBytes => (_end - _start) + (_bitsInBuffer / 8);
- /// <summary>Ensure that count bits are in the bit buffer.</summary>
- /// <param name="count">Can be up to 16.</param>
- /// <returns>Returns false if input is not sufficient to make this true.</returns>
- public bool EnsureBitsAvailable(int count)
- {
- Debug.Assert(0 < count && count <= 16, "count is invalid.");
- // manual inlining to improve perf
- if (_bitsInBuffer < count)
- {
- if (NeedsInput())
- {
- return false;
- }
- // insert a byte to bitbuffer
- _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
- _bitsInBuffer += 8;
- if (_bitsInBuffer < count)
- {
- if (NeedsInput())
- {
- return false;
- }
- // insert a byte to bitbuffer
- _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
- _bitsInBuffer += 8;
- }
- }
- return true;
- }
- /// <summary>
- /// This function will try to load 16 or more bits into bitBuffer.
- /// It returns whatever is contained in bitBuffer after loading.
- /// The main difference between this and GetBits is that this will
- /// never return -1. So the caller needs to check AvailableBits to
- /// see how many bits are available.
- /// </summary>
- public uint TryLoad16Bits()
- {
- if (_bitsInBuffer < 8)
- {
- if (_start < _end)
- {
- _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
- _bitsInBuffer += 8;
- }
- if (_start < _end)
- {
- _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
- _bitsInBuffer += 8;
- }
- }
- else if (_bitsInBuffer < 16)
- {
- if (_start < _end)
- {
- _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
- _bitsInBuffer += 8;
- }
- }
- return _bitBuffer;
- }
- private uint GetBitMask(int count) => ((uint)1 << count) - 1;
- /// <summary>Gets count bits from the input buffer. Returns -1 if not enough bits available.</summary>
- public int GetBits(int count)
- {
- Debug.Assert(0 < count && count <= 16, "count is invalid.");
- if (!EnsureBitsAvailable(count))
- {
- return -1;
- }
- int result = (int)(_bitBuffer & GetBitMask(count));
- _bitBuffer >>= count;
- _bitsInBuffer -= count;
- return result;
- }
- /// <summary>
- /// Copies length bytes from input buffer to output buffer starting at output[offset].
- /// You have to make sure, that the buffer is byte aligned. If not enough bytes are
- /// available, copies fewer bytes.
- /// </summary>
- /// <returns>Returns the number of bytes copied, 0 if no byte is available.</returns>
- public int CopyTo(byte[] output, int offset, int length)
- {
- Debug.Assert(output != null);
- Debug.Assert(offset >= 0);
- Debug.Assert(length >= 0);
- Debug.Assert(offset <= output.Length - length);
- Debug.Assert((_bitsInBuffer % 8) == 0);
- // Copy the bytes in bitBuffer first.
- int bytesFromBitBuffer = 0;
- while (_bitsInBuffer > 0 && length > 0)
- {
- output[offset++] = (byte)_bitBuffer;
- _bitBuffer >>= 8;
- _bitsInBuffer -= 8;
- length--;
- bytesFromBitBuffer++;
- }
- if (length == 0)
- {
- return bytesFromBitBuffer;
- }
- int avail = _end - _start;
- if (length > avail)
- {
- length = avail;
- }
- Array.Copy(_buffer, _start, output, offset, length);
- _start += length;
- return bytesFromBitBuffer + length;
- }
- /// <summary>
- /// Return true is all input bytes are used.
- /// This means the caller can call SetInput to add more input.
- /// </summary>
- public bool NeedsInput() => _start == _end;
- /// <summary>
- /// Set the byte array to be processed.
- /// All the bits remained in bitBuffer will be processed before the new bytes.
- /// We don't clone the byte array here since it is expensive.
- /// The caller should make sure after a buffer is passed in.
- /// It will not be changed before calling this function again.
- /// </summary>
- public void SetInput(byte[] buffer, int offset, int length)
- {
- Debug.Assert(buffer != null);
- Debug.Assert(offset >= 0);
- Debug.Assert(length >= 0);
- Debug.Assert(offset <= buffer.Length - length);
- Debug.Assert(_start == _end);
- _buffer = buffer;
- _start = offset;
- _end = offset + length;
- }
- /// <summary>Skip n bits in the buffer.</summary>
- public void SkipBits(int n)
- {
- Debug.Assert(_bitsInBuffer >= n, "No enough bits in the buffer, Did you call EnsureBitsAvailable?");
- _bitBuffer >>= n;
- _bitsInBuffer -= n;
- }
- /// <summary>Skips to the next byte boundary.</summary>
- public void SkipToByteBoundary()
- {
- _bitBuffer >>= (_bitsInBuffer % 8);
- _bitsInBuffer = _bitsInBuffer - (_bitsInBuffer % 8);
- }
- }
- }
|